EventTrigger로 속성 설정
EventTrigger를 사용하여 속성을 설정할 수 있기를 원합니다. 여기에는 여러 가지 문제가 있습니다.
1) EventTriggers는 액션 만 지원하므로 스토리 보드를 사용하여 속성을 설정해야합니다.
2) 스토리 보드를 사용하면 두 가지 옵션이 있습니다.
- 중지 : 애니메이션이 중지되면 값이 애니메이션이 시작되기 전으로 되돌아갑니다.
- HoldEnd : 속성을 잠그므로 코드 나 사용자 상호 작용이 애니메이션이 보유하고있는 속성을 변경할 수 없습니다.
아래 예제에서는 버튼을 클릭 할 때 IsChecked 속성을 False로 설정하고 사용자가 IsChecked를 변경할 수 있도록하거나 코드에서 속성을 변경할 수 있기를 원합니다.
예:
<EventTrigger
SourceName="myButton"
RoutedEvent="Button.Click">
<EventTrigger.Actions>
<BeginStoryboard>
<Storyboard>
<BooleanAnimationUsingKeyFrames
Storyboard.TargetName="myCheckBox"
Storyboard.TargetProperty="IsChecked"
FillBehavior="Stop">
<DiscreteBooleanKeyFrame
KeyTime="00:00:00"
Value="False" />
</BooleanAnimationUsingKeyFrames>
</Storyboard>
</BeginStoryboard>
</EventTrigger.Actions>
</EventTrigger>
스토리 보드가 완료된 후 "Completed"이벤트를 사용하여 값을 False로 설정할 수 있음을 알고 있습니다. 그러나이 논리는 사용자 지정 컨트롤에 사용되며 UI에만 적용되므로이 경우 XAML 내에 논리를 포함하고 싶습니다.
XAML을 좋아하는만큼 이런 종류의 작업을 위해 코드 숨김으로 전환합니다. 연결된 행동 은 이에 대한 좋은 패턴입니다. Expression Blend 3 은 동작을 프로그래밍하고 사용 하는 표준 방법 을 제공합니다 . 있다 몇 기존 식 커뮤니티 사이트에이.
자신 만의 액션을 만드세요.
namespace WpfUtil
{
using System.Reflection;
using System.Windows;
using System.Windows.Interactivity;
/// <summary>
/// Sets the designated property to the supplied value. TargetObject
/// optionally designates the object on which to set the property. If
/// TargetObject is not supplied then the property is set on the object
/// to which the trigger is attached.
/// </summary>
public class SetPropertyAction : TriggerAction<FrameworkElement>
{
// PropertyName DependencyProperty.
/// <summary>
/// The property to be executed in response to the trigger.
/// </summary>
public string PropertyName
{
get { return (string)GetValue(PropertyNameProperty); }
set { SetValue(PropertyNameProperty, value); }
}
public static readonly DependencyProperty PropertyNameProperty
= DependencyProperty.Register("PropertyName", typeof(string),
typeof(SetPropertyAction));
// PropertyValue DependencyProperty.
/// <summary>
/// The value to set the property to.
/// </summary>
public object PropertyValue
{
get { return GetValue(PropertyValueProperty); }
set { SetValue(PropertyValueProperty, value); }
}
public static readonly DependencyProperty PropertyValueProperty
= DependencyProperty.Register("PropertyValue", typeof(object),
typeof(SetPropertyAction));
// TargetObject DependencyProperty.
/// <summary>
/// Specifies the object upon which to set the property.
/// </summary>
public object TargetObject
{
get { return GetValue(TargetObjectProperty); }
set { SetValue(TargetObjectProperty, value); }
}
public static readonly DependencyProperty TargetObjectProperty
= DependencyProperty.Register("TargetObject", typeof(object),
typeof(SetPropertyAction));
// Private Implementation.
protected override void Invoke(object parameter)
{
object target = TargetObject ?? AssociatedObject;
PropertyInfo propertyInfo = target.GetType().GetProperty(
PropertyName,
BindingFlags.Instance|BindingFlags.Public
|BindingFlags.NonPublic|BindingFlags.InvokeMethod);
propertyInfo.SetValue(target, PropertyValue);
}
}
}
이 경우 내 viewmodel에서 DialogResult라는 속성에 바인딩합니다.
<Grid>
<Button>
<i:Interaction.Triggers>
<i:EventTrigger EventName="Click">
<wpf:SetPropertyAction PropertyName="DialogResult" TargetObject="{Binding}"
PropertyValue="{x:Static mvvm:DialogResult.Cancel}"/>
</i:EventTrigger>
</i:Interaction.Triggers>
Cancel
</Button>
</Grid>
값을 지정할 때 xaml이 덜 장황 해 보이도록 Neutrino의 솔루션을 수정했습니다.
렌더링 된 xaml의 사진이 없어서 죄송합니다. 클릭하는 [=] 햄버거 버튼이 [<-] 뒤로 버튼으로 바뀌고 그리드의 가시성을 전환한다고 상상해보세요.
xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
...
<Grid>
<Button x:Name="optionsButton">
<i:Interaction.Triggers>
<i:EventTrigger EventName="Click">
<local:SetterAction PropertyName="Visibility" Value="Collapsed" />
<local:SetterAction PropertyName="Visibility" TargetObject="{Binding ElementName=optionsBackButton}" Value="Visible" />
<local:SetterAction PropertyName="Visibility" TargetObject="{Binding ElementName=optionsPanel}" Value="Visible" />
</i:EventTrigger>
</i:Interaction.Triggers>
<glyphs:Hamburger Width="10" Height="10" />
</Button>
<Button x:Name="optionsBackButton" Visibility="Collapsed">
<i:Interaction.Triggers>
<i:EventTrigger EventName="Click">
<local:SetterAction PropertyName="Visibility" Value="Collapsed" />
<local:SetterAction PropertyName="Visibility" TargetObject="{Binding ElementName=optionsButton}" Value="Visible" />
<local:SetterAction PropertyName="Visibility" TargetObject="{Binding ElementName=optionsPanel}" Value="Collapsed" />
</i:EventTrigger>
</i:Interaction.Triggers>
<glyphs:Back Width="12" Height="11" />
</Button>
</Grid>
...
<Grid Grid.RowSpan="2" x:Name="optionsPanel" Visibility="Collapsed">
</Grid>
Neutrino의 솔루션과 같이 다음과 같이 값을 지정할 수도 있습니다.
<Button x:Name="optionsButton">
<i:Interaction.Triggers>
<i:EventTrigger EventName="Click">
<local:SetterAction PropertyName="Visibility" Value="{x:Static Visibility.Collapsed}" />
<local:SetterAction PropertyName="Visibility" TargetObject="{Binding ElementName=optionsBackButton}" Value="{x:Static Visibility.Visible}" />
<local:SetterAction PropertyName="Visibility" TargetObject="{Binding ElementName=optionsPanel}" Value="{x:Static Visibility.Visible}" />
</i:EventTrigger>
</i:Interaction.Triggers>
<glyphs:Hamburger Width="10" Height="10" />
</Button>
그리고 여기에 코드가 있습니다.
using System;
using System.ComponentModel;
using System.Reflection;
using System.Windows;
using System.Windows.Interactivity;
namespace Mvvm.Actions
{
/// <summary>
/// Sets a specified property to a value when invoked.
/// </summary>
public class SetterAction : TargetedTriggerAction<FrameworkElement>
{
#region Properties
#region PropertyName
/// <summary>
/// Property that is being set by this setter.
/// </summary>
public string PropertyName
{
get { return (string)GetValue(PropertyNameProperty); }
set { SetValue(PropertyNameProperty, value); }
}
public static readonly DependencyProperty PropertyNameProperty =
DependencyProperty.Register("PropertyName", typeof(string), typeof(SetterAction),
new PropertyMetadata(String.Empty));
#endregion
#region Value
/// <summary>
/// Property value that is being set by this setter.
/// </summary>
public object Value
{
get { return (object)GetValue(ValueProperty); }
set { SetValue(ValueProperty, value); }
}
public static readonly DependencyProperty ValueProperty =
DependencyProperty.Register("Value", typeof(object), typeof(SetterAction),
new PropertyMetadata(null));
#endregion
#endregion
#region Overrides
protected override void Invoke(object parameter)
{
var target = TargetObject ?? AssociatedObject;
var targetType = target.GetType();
var property = targetType.GetProperty(PropertyName, BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Static | BindingFlags.Instance);
if (property == null)
throw new ArgumentException(String.Format("Property not found: {0}", PropertyName));
if (property.CanWrite == false)
throw new ArgumentException(String.Format("Property is not settable: {0}", PropertyName));
object convertedValue;
if (Value == null)
convertedValue = null;
else
{
var valueType = Value.GetType();
var propertyType = property.PropertyType;
if (valueType == propertyType)
convertedValue = Value;
else
{
var propertyConverter = TypeDescriptor.GetConverter(propertyType);
if (propertyConverter.CanConvertFrom(valueType))
convertedValue = propertyConverter.ConvertFrom(Value);
else if (valueType.IsSubclassOf(propertyType))
convertedValue = Value;
else
throw new ArgumentException(String.Format("Cannot convert type '{0}' to '{1}'.", valueType, propertyType));
}
}
property.SetValue(target, convertedValue);
}
#endregion
}
}
Storyboard 중지는 필요에 따라 코드 숨김 또는 xaml에서 수행 할 수 있습니다.
EventTrigger가 버튼 외부로 이동하면 스토리 보드에 중지를 알리는 다른 EventTrigger로 대상을 지정할 수 있습니다. 이런 방식으로 스토리 보드가 중지되면 이전 값으로 돌아 가지 않습니다.
Here I've moved the Button.Click EventTrigger to a surrounding StackPanel and added a new EventTrigger on the the CheckBox.Click to stop the Button's storyboard when the CheckBox is clicked. This lets us check and uncheck the CheckBox when it is clicked on and gives us the desired unchecking behavior from the button as well.
<StackPanel x:Name="myStackPanel">
<CheckBox x:Name="myCheckBox"
Content="My CheckBox" />
<Button Content="Click to Uncheck"
x:Name="myUncheckButton" />
<Button Content="Click to check the box in code."
Click="OnClick" />
<StackPanel.Triggers>
<EventTrigger RoutedEvent="Button.Click"
SourceName="myUncheckButton">
<EventTrigger.Actions>
<BeginStoryboard x:Name="myBeginStoryboard">
<Storyboard x:Name="myStoryboard">
<BooleanAnimationUsingKeyFrames Storyboard.TargetName="myCheckBox"
Storyboard.TargetProperty="IsChecked">
<DiscreteBooleanKeyFrame KeyTime="00:00:00"
Value="False" />
</BooleanAnimationUsingKeyFrames>
</Storyboard>
</BeginStoryboard>
</EventTrigger.Actions>
</EventTrigger>
<EventTrigger RoutedEvent="CheckBox.Click"
SourceName="myCheckBox">
<EventTrigger.Actions>
<StopStoryboard BeginStoryboardName="myBeginStoryboard" />
</EventTrigger.Actions>
</EventTrigger>
</StackPanel.Triggers>
</StackPanel>
To stop the storyboard in the code behind, we will have to do something slightly different. The third button provides the method where we will stop the storyboard and set the IsChecked property back to true through code.
We can't call myStoryboard.Stop() because we did not begin the Storyboard through the code setting the isControllable parameter. Instead, we can remove the Storyboard. To do this we need the FrameworkElement that the storyboard exists on, in this case our StackPanel. Once the storyboard is removed, we can once again set the IsChecked property with it persisting to the UI.
private void OnClick(object sender, RoutedEventArgs e)
{
myStoryboard.Remove(myStackPanel);
myCheckBox.IsChecked = true;
}
참고URL : https://stackoverflow.com/questions/942548/setting-a-property-with-an-eventtrigger
'code' 카테고리의 다른 글
.ix ()가 더 빠르고 정수 및 레이블 액세스를 지원하기 때문에 .loc () 및 .iloc ()보다 항상 낫습니까? (0) | 2020.11.12 |
---|---|
null 대신 EventArgs.Empty를 사용하는 이유는 무엇입니까? (0) | 2020.11.12 |
Excel VBA에서 JSON 구문 분석 (0) | 2020.11.12 |
HTTP 헤더의 Set-Cookie는 AngularJS에서 무시됩니다. (0) | 2020.11.12 |
YouTube의 오류 500 페이지 디코딩 (0) | 2020.11.12 |