EventTrigger를 사용하여 속성 설정
Event Trigger로 속성을 설정할 수 있으면 좋겠는데, 여기에는 몇 가지 문제가 있습니다.
1) EventTrigger는 액션만 지원하므로 StoryBoard를 사용하여 속성을 설정해야 합니다.
2) 스토리보드를 사용하면 다음 두 가지 옵션이 있습니다.
- 중지: 애니메이션이 중지되면 값이 애니메이션 시작 이전으로 되돌아갑니다.
- 홀드 엔드:이렇게 하면 속성이 잠기므로 코드나 사용자 상호 작용이 애니메이션이 보유하고 있는 속성을 변경할 수 없습니다.
다음 예에서는 버튼을 클릭했을 때 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로 설정할 수 있습니다.단, 이 경우 XAML 내의 로직은 커스텀컨트롤에서 사용되며 UI에만 한정되기 때문에 이 로직을 포함시키고 싶습니다.
자신만의 동작을 만들어보세요.
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);
}
}
}
이 경우 뷰 모델에서 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을 매우 좋아하지만, 이러한 작업에는 코드 배후에 전환합니다.부가된 행동은 이를 위한 좋은 패턴입니다.Expression Blend 3은 동작을 프로그래밍하고 사용하는 표준 방법을 제공합니다.Expression Community Site에는 몇 가지 기존 항목이 있습니다.
값을 지정할 때 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
}
}
편집: Interactivity dll은 Blend의 일부가 아니라 "Microsoft"가 되었습니다.젬, 행동가들Wpf" NuGet 패키지.코드 리스트는 https://github.com/microsoft/XamlBehaviorsWpf 입니다.
참조: https://devblogs.microsoft.com/dotnet/open-sourcing-xaml-behaviors-for-wpf/
이전 Blend Microsoft에서 마이그레이션하는 단계입니다.표현.interactions.dll to new opensource Interactivity dll (이전 메모가 정확했으면 합니다;p):
1. Install the "Microsoft.Xaml.Behaviors.Wpf" NuGet package.
2. Edit xaml files:
Replace 'http://schemas.microsoft.com/expression/2010/interactivity' and
'http://schemas.microsoft.com/expression/2010/interactions'
with 'http://schemas.microsoft.com/xaml/behaviors'.
Replace 'xmlns:ei="clr-namespace:Microsoft.Expression.Interactivity.Core;assembly=Microsoft.Expression.Interactions"' and
'xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"'
with 'xmlns:i="http://schemas.microsoft.com/xaml/behaviors"'.
3. Edit C# files:
Replace usings in c# files 'Microsoft.Xaml.Interactivity' and
'Microsoft.Xaml.Interactions'
with 'Microsoft.Xaml.Behaviors'.
Remove references to 'Microsoft.Expression.Interactions' and
'System.Windows.Interactivity'.
Storyboard를 정지하는 방법은 필요에 따라 뒤에 있는 코드 또는 xaml로 할 수 있습니다.
EventTrigger가 버튼 밖으로 이동하면 스토리보드에 중지하라는 메시지가 표시되는 다른 EventTrigger를 통해 EventTrigger를 대상으로 지정할 수 있습니다.이 방법으로 스토리보드를 정지해도 이전 값으로 돌아가지 않습니다.
여기 버튼을 옮겼습니다.EventTrigger를 클릭하여 주변의 StackPanel에 새 EventTrigger를 CheckBox에 추가합니다.CheckBox를 클릭하면 버튼의 스토리보드가 정지됩니다.그러면 CheckBox를 클릭할 때 CheckBox를 켜고 끌 수 있으며 버튼에서 원하는 선택 취소 동작을 할 수도 있습니다.
<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>
뒤에 있는 코드의 스토리보드를 멈추려면 조금 다른 작업을 해야 합니다.세 번째 버튼은 스토리보드를 정지하고 코드를 통해 Is Checked 속성을 true로 설정하는 방법을 제공합니다.
myStoryboard를 호출할 수 없습니다.stop()은 isControlable 파라미터 설정 코드를 사용하여 스토리보드를 시작하지 않았기 때문입니다.대신 스토리보드를 제거할 수 있습니다.그러기 위해서는 스토리보드가 존재하는 FrameworkElement(이 경우는 StackPanel)가 필요합니다.스토리보드가 삭제되면 IsChecked 속성을 다시 설정하여 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
'programing' 카테고리의 다른 글
cURL이 에러 「(23) Failed write body」를 반환하는 이유는 무엇입니까? (0) | 2023.04.11 |
---|---|
배열 또는 두 날짜 사이의 모든 날짜 목록 만들기 (0) | 2023.04.11 |
문자열 배열에 문자열이 포함되어 있는지 확인하기 위해 C# 사용 (0) | 2023.04.11 |
명령어를 WPF 텍스트블록에 추가하려면 어떻게 해야 하나요? (0) | 2023.04.11 |
각 GROUP BY 그룹에서 첫 번째 행을 선택하시겠습니까? (0) | 2023.04.11 |