属性更改时如何更改任何控件的外观?

时间:2010-10-07 15:06:12

标签: c# wpf wpf-controls binding expression-blend

我想向用户强调任何已经修改的内容,以便他们知道他们已经改变了什么或者在幕后以编程方式改变了什么。

我想使用样式将此逻辑应用于我的所有控件,但我不确定如何。我知道我需要创建一个触发器,但不确定要准确触发什么,或者如何获取绑定属性的任何更改,以便知道它是否已更改。

感谢。

3 个答案:

答案 0 :(得分:2)

可能最好的方法是为您的值编写一个包装类,它实现INotifyPropertyChanged并公开类型为Value的读/写object属性和一个布尔ValueHasChanged属性。然后,您可以在Value setter:

中轻松更改跟踪
if (!value.Equals(_Value))
{
   _Value = value;
   ValueHasChanged=true;
   OnPropertyChanged("Value");
}

它应该公开包含其内部字段的string属性,而不是展示DateTimeValueWrapper属性的视图模型类,例如:

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"

答案 1 :(得分:2)

我仍然掌握着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>

答案 2 :(得分:-1)

我会重新阅读有关名为INotifyPropertyChanged

的设计模式的内容

另外一件事,当你在一个类框架上,你不再在UI线程中,因此你应该使用与UI线程相关的调度程序看看这个

dispatcher on msdn

假设您要打印新板,您应该这样做

  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>

所以当按下按钮时,它将改变它的内容可见性。