【发布时间】:2010-10-07 15:06:11
【问题描述】:
我想向用户突出显示已修改的任何内容,以便他们知道他们已更改了什么或在后台为他们以编程方式更改了哪些内容。
我想使用样式将此逻辑应用于我的所有控件,但我不确定如何。我知道我需要创建一个触发器,但不确定要准确触发什么或如何获取对绑定属性的任何更改,以便知道它是否已更改。
谢谢。
【问题讨论】:
标签: c# wpf wpf-controls binding expression-blend
我想向用户突出显示已修改的任何内容,以便他们知道他们已更改了什么或在后台为他们以编程方式更改了哪些内容。
我想使用样式将此逻辑应用于我的所有控件,但我不确定如何。我知道我需要创建一个触发器,但不确定要准确触发什么或如何获取对绑定属性的任何更改,以便知道它是否已更改。
谢谢。
【问题讨论】:
标签: c# wpf wpf-controls binding expression-blend
可能最好的方法是为您的值编写一个包装类,该类实现INotifyPropertyChanged 并公开object 类型的读/写Value 属性和布尔ValueHasChanged 属性。然后,您可以在 Value setter 中轻松进行更改跟踪:
if (!value.Equals(_Value))
{
_Value = value;
ValueHasChanged=true;
OnPropertyChanged("Value");
}
您的视图模型类不应公开 string 或 DateTime 属性,而应公开包装其内部字段的 ValueWrapper 属性,例如:
private string SomeStringField;
private ValueWrapper _SomeStringProperty;
public ValueWrapper SomeStringProperty
{
get
{
return (_SomeStringProperty == null)
? _SomeStringProperty = new ValueWrapper(SomeStringField)
: _SomeStringProperty;
}
}
然后你可以建立一个风格像:
<Style x:Key="ShowChangedValue">
<Setter Property="Background" Value="White"/>
<Style.Triggers>
<DataTrigger Binding="{Binding ValueHasChanged}" Value="True">
<Setter Property="Background" Value="AliceBlue"/>
</DataTrigger>
</Style.Triggers>
</Style>
并像这样使用它:
<TextBox DataContext="{Binding SomeStringProperty}"
Text="{Binding Value, Mode=TwoWay}"
Style="{StaticResource ShowChangedValue}"/>
这有一些令人讨厌的事情,例如要更改主视图模型类中的属性值,您现在必须使用SomeStringProperty.Value = "foo" 而不是SomeStringProperty = "foo"。
【讨论】:
我仍在掌握 WPF 的窍门,因此可能有更好的方法,但这是我认为可行的方法。
创建一个ValueTracker类,该类将提供以下3个附加依赖属性
TrackProperty - 这将是应跟踪的控件的属性
DefaultValue - 这是被视为默认值的值,否则会触发 Style 触发器。
IsDefaultValue - 这将指示当前值是否与默认值匹配,此属性将用于触发测试。
这是一个快速测试,它并不完美,但我相信稍微调整一下会让事情顺利进行,当然有更多 WPF 经验的人可以改进这个想法。
using System;
using System.Windows;
using System.ComponentModel;
namespace WpfApplication1
{
public static class ValueTracker
{
// Attached dependency property for DefaultValue
public static object GetDefaultValue(DependencyObject obj)
{
return (object)obj.GetValue(DefaultValueProperty);
}
public static void SetDefaultValue(DependencyObject obj, object value)
{
obj.SetValue(DefaultValueProperty, value);
}
public static readonly DependencyProperty DefaultValueProperty =
DependencyProperty.RegisterAttached("DefaultValue",
typeof(object), typeof(ValueTracker), new UIPropertyMetadata(0));
// Attached dependency property for IsDefaultValue
public static bool GetIsDefaultValue(DependencyObject obj)
{
return (bool)obj.GetValue(IsDefaultValueProperty);
}
private static void SetIsDefaultValue(DependencyObject obj, bool value)
{
obj.SetValue(IsDefaultValueProperty, value);
}
public static readonly DependencyProperty IsDefaultValueProperty =
DependencyProperty.RegisterAttached("IsDefaultValue",
typeof(bool), typeof(ValueTracker), new UIPropertyMetadata(false));
// Attached dependency property for TrackedProperty
public static DependencyProperty GetTrackProperty(DependencyObject obj)
{
return (DependencyProperty)obj.GetValue(TrackPropertyProperty);
}
public static void SetTrackProperty(DependencyObject obj, DependencyProperty value)
{
obj.SetValue(TrackPropertyProperty, value);
}
public static readonly DependencyProperty TrackPropertyProperty =
DependencyProperty.RegisterAttached("TrackProperty",
typeof(DependencyProperty), typeof(ValueTracker),
new UIPropertyMetadata(TrackPropertyChanged));
public static void TrackPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
DependencyProperty oldProperty = e.OldValue as DependencyProperty;
if (oldProperty != null)
{
DependencyPropertyDescriptor dpd =
DependencyPropertyDescriptor.FromProperty(oldProperty, typeof(UIElement));
if (dpd != null)
{
dpd.RemoveValueChanged(d, TrackedPropertyValueChanged);
}
}
DependencyProperty newProperty = e.NewValue as DependencyProperty;
if (newProperty != null)
{
DependencyPropertyDescriptor dpd =
DependencyPropertyDescriptor.FromProperty(newProperty, typeof(UIElement));
if (dpd != null)
{
dpd.AddValueChanged(d, TrackedPropertyValueChanged);
}
}
}
private static void TrackedPropertyValueChanged(object sender, EventArgs e)
{
DependencyObject o = sender as DependencyObject;
if (o != null)
{
object defaultValue = Convert.ChangeType(GetDefaultValue(o), GetTrackProperty(o).PropertyType);
SetIsDefaultValue(o, Object.Equals(o.GetValue(GetTrackProperty(o)), defaultValue));
}
}
}
}
以上可以如下使用。
1- 创建一个在 ValueTracker.IsDefaultValue 上触发的样式
2- 对于要跟踪的每个控件,附加样式并设置 ValueTracker.DefaultValue 并使用 ValueTracker.TrackProperty 设置应跟踪的属性。
<Window x:Class="WpfApplication1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:r="clr-namespace:WpfApplication1"
mc:Ignorable="d" xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:WpfApplication1"
Title="MainWindow"
d:DesignHeight="221" d:DesignWidth="287"
Width="250" Height="250">
<StackPanel Loaded="StackPanel_Loaded" >
<StackPanel.Resources>
<Style TargetType="{x:Type Control}" x:Key="trackChanges">
<Style.Triggers>
<Trigger Property="local:ValueTracker.IsDefaultValue" Value="false">
<Setter Property="FontWeight" Value="Bold" />
</Trigger>
</Style.Triggers>
</Style>
</StackPanel.Resources>
<TextBox Name="textbox1" Width="100" Height="23"
local:ValueTracker.DefaultValue="Help"
local:ValueTracker.TrackProperty="TextBox.Text"
Style="{StaticResource ResourceKey=trackChanges}" />
<ComboBox Name="combobox1"
SelectedIndex="2"
local:ValueTracker.DefaultValue="2"
local:ValueTracker.TrackProperty="ComboBox.SelectedIndex"
Style="{StaticResource ResourceKey=trackChanges}">
<ComboBox.Items>
<ComboBoxItem>Item 1</ComboBoxItem>
<ComboBoxItem>Item 2</ComboBoxItem>
<ComboBoxItem>Item 3</ComboBoxItem>
<ComboBoxItem>Item 4</ComboBoxItem>
</ComboBox.Items>
</ComboBox>
</StackPanel>
</Window>
【讨论】:
我会推荐阅读名为 INotifyPropertyChanged 的设计模式
另一件事,当你在一个类框架上时,你不再在 UI 线程中,因此你应该使用与 UI 线程相关的调度程序,看看这个
假设你想打印新板,你应该这样做
printDelgate paintControlDelgate = () => paintControl();
m_CurrentDispatcher.Invoke(paintControlDelgate);
让我们假设你的代码中有一个按钮
<ControlTemplate x:Key="StarTemplate" TargetType="{x:Type Button}">
<Grid>
<ed:RegularPolygon Visibility="Collapsed" Name="star1" Fill="{Binding Path=ButtonColor}"
InnerRadius="0.47211" Margin="20.5,16,15.5,8" PointCount="5" Stroke="Black"
StrokeThickness="2" Height="40" Width="40"/>
<ed:RegularPolygon Name="star2" Fill="Black" Visibility="Visible" InnerRadius="0.47211" Margin="20.5,16,15.5,8"
PointCount="5" Stroke="Black" StrokeThickness="6" Height="40" Width="40"/>
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="IsPressed" Value="True">
<Setter TargetName="star1" Property="Visibility" Value="Visible"/>
<Setter TargetName="star2" Property="Visibility" Value="Collapsed"/>
</Trigger>
所以当按钮被按下时它会改变它的内容可见性。
【讨论】: