【问题标题】:Best practice for changing TextBox Border color?更改文本框边框颜色的最佳实践?
【发布时间】:2013-03-09 10:18:33
【问题描述】:

假设您有一个包含多个这样的 Texbox 的视图

<TextBox Text="{Binding myText1, UpdateSourceTrigger=PropertyChanged, ValidatesOnDataErrors=True}" />

每个都已经包含一些文本。如果用户更改此文本,则文本框边框应更改为橙色,如果他撤消更改,则应获得默认颜色。

目前我是这样做的

<TextBox Height="23"  Text="{Binding myText1, UpdateSourceTrigger=PropertyChanged, ValidatesOnDataErrors=True}" BorderThickness="2">
   <TextBox.Style>
        <Style TargetType="TextBox">
             <Style.Triggers>
                 <DataTrigger Binding="{Binding myDirtyText1, UpdateSourceTrigger=PropertyChanged}" Value="True">
                     <Setter Property="BorderBrush" Value="Orange"/>
                 </DataTrigger>
             </Style.Triggers>
        </Style>
    </TextBox.Style>
</TextBox>

有没有更通用/更简单的方法来做到这一点?

编辑

我已经在使用IDataErrorInfo + System.ComponentModel.DataAnnotations 进行错误验证。在这种情况下可能有类似的方法,但我没有发现任何有用的东西可以将我的 xaml 和代码减少到最低限度。

编辑 2.0

我认为你并没有真正理解我的问题,所以我将提供一个更好的示例来说明它的实际外观:

查看 Xaml(无代码隐藏)

<Grid Margin="12">
    <Label Content="Name:" Height="28" HorizontalAlignment="Left" VerticalAlignment="Top" Width="79" />
    <TextBox Height="23" HorizontalAlignment="Left" Margin="102,2,0,0" VerticalAlignment="Top" Width="170" BorderThickness="2"
             Text="{Binding NameD, UpdateSourceTrigger=PropertyChanged, ValidatesOnDataErrors=True}">
        <TextBox.Style>
            <Style TargetType="TextBox">
                <Style.Triggers>
                    <DataTrigger Binding="{Binding dirtyName, UpdateSourceTrigger=PropertyChanged}" Value="True">
                        <Setter Property="BorderBrush" Value="Orange"/>
                    </DataTrigger>
                </Style.Triggers>
            </Style>
        </TextBox.Style>
    </TextBox>

    <Label Content="Anzeigetext:" Height="28" HorizontalAlignment="Left" Margin="0,34,0,0" VerticalAlignment="Top" Width="79" />
    <TextBox BorderThickness="2" Height="23" HorizontalAlignment="Left" Margin="102,36,0,0" VerticalAlignment="Top" Width="170" 
             Text="{Binding AnzeigetextD, UpdateSourceTrigger=PropertyChanged, ValidatesOnDataErrors=True}">
        <TextBox.Style>
            <Style TargetType="TextBox">
                <Style.Triggers>
                    <DataTrigger Binding="{Binding dirtyAnzeigetext, UpdateSourceTrigger=PropertyChanged}" Value="True">
                        <Setter Property="BorderBrush" Value="Orange"/>
                    </DataTrigger>
                </Style.Triggers>
            </Style>
        </TextBox.Style>
    </TextBox>

    <Label Content="Preis:" Height="28" HorizontalAlignment="Left" Margin="0,68,0,0" VerticalAlignment="Top" Width="79" />
    <TextBox BorderThickness="2" Height="23" HorizontalAlignment="Left" Margin="102,70,0,0" VerticalAlignment="Top" Width="170" 
             Text="{Binding PreisD, UpdateSourceTrigger=PropertyChanged, ValidatesOnDataErrors=True, StringFormat=\{0:c\}}">
        <TextBox.Style>
            <Style TargetType="TextBox">
                <Style.Triggers>
                    <DataTrigger Binding="{Binding dirtyPreis, UpdateSourceTrigger=PropertyChanged}" Value="True">
                        <Setter Property="BorderBrush" Value="Orange"/>
                    </DataTrigger>
                </Style.Triggers>
            </Style>
        </TextBox.Style>
    </TextBox>

    <Button Content="Speichern" Height="23" HorizontalAlignment="Left" Margin="102,110,0,0" VerticalAlignment="Top" Width="75" Command="{Binding SaveCommand}"/>
    <Button Content="Abbrechen" Height="23" HorizontalAlignment="Left" Margin="197,110,0,0" VerticalAlignment="Top" Width="75" Command="{Binding CancelCommand}"/>
</Grid>

视图模型

public class MenuangebotVM : DetailVM, IContains
{
    #region private Values
    private Menuangebot myOriginal = new Menuangebot();
    private Menuangebot myValue = new Menuangebot();
    #endregion // private Values

    #region Properties

    #region Detail Properties
    public int Id { get { return myOriginal.Id; } }
    public bool? Result { get; private set; }
    public string Beschreibung { get { return "Einrichtung"; } }

    [Required]
    [RegularExpression(@"^[0-9a-zA-ZäöüßÄÖÜß''-'\s]{2,40}$")]
    public string NameD
    {
        get { return myValue.Name; }
        set
        {
            myValue.Name = value;
            RaisePropertyChanged(() => Reg(() => NameD));
            RaisePropertyChanged(() => Reg(() => dirtyName));
        }
    }
    public bool dirtyName
    {
        get { return (!isNew && myValue.Name != myOriginal.Name) ? true : false; }
    }

    [Required]
    [RegularExpression(@"^[0-9a-zA-ZäöüßÄÖÜß''-'\s]{2,25}$")]
    public string AnzeigetextD
    {
        get { return myValue.Anzeigetext; }
        set
        {
            myValue.Anzeigetext = value;
            RaisePropertyChanged(() => Reg(() => AnzeigetextD));
            RaisePropertyChanged(() => Reg(() => dirtyAnzeigetext));
        }
    }
    public bool dirtyAnzeigetext
    {
        get { return (!isNew && myValue.Anzeigetext != myOriginal.Anzeigetext) ? true : false; }
    }

    [Required]
    public decimal PreisD
    {
        get { return myValue.Preis; }
        set
        {
            myValue.Preis = value;
            RaisePropertyChanged(() => Reg(() => PreisD));
            RaisePropertyChanged(() => Reg(() => dirtyPreis));
        }
    }
    public bool dirtyPreis
    {
        get
        {
            var value = myValue.Preis;
            var Original = myOriginal.Preis;

            return (!isNew && value != Original) ? true : false;
        }
    }

    #endregion //Detail Properties
    #endregion //Properties

// more code

}

我期望的应该是这样的

查看

<Grid Margin="12">
    <Label Content="Name:" Height="28" HorizontalAlignment="Left" VerticalAlignment="Top" Width="79" />
    <TextBox Height="23" HorizontalAlignment="Left" Margin="102,2,0,0" VerticalAlignment="Top" Width="170" BorderThickness="2"
             Text="{Binding NameD, UpdateSourceTrigger=PropertyChanged, ValidatesOnDataErrors=True, EditesOnDataChanges=true}">

    </TextBox>

    <Label Content="Anzeigetext:" Height="28" HorizontalAlignment="Left" Margin="0,34,0,0" VerticalAlignment="Top" Width="79" />
    <TextBox BorderThickness="2" Height="23" HorizontalAlignment="Left" Margin="102,36,0,0" VerticalAlignment="Top" Width="170" 
             Text="{Binding AnzeigetextD, UpdateSourceTrigger=PropertyChanged, ValidatesOnDataErrors=True, EditesOnDataChanges=true}">
    </TextBox>

    <Label Content="Preis:" Height="28" HorizontalAlignment="Left" Margin="0,68,0,0" VerticalAlignment="Top" Width="79" />
    <TextBox BorderThickness="2" Height="23" HorizontalAlignment="Left" Margin="102,70,0,0" VerticalAlignment="Top" Width="170" 
             Text="{Binding PreisD, UpdateSourceTrigger=PropertyChanged, ValidatesOnDataErrors=True, StringFormat=\{0:c\, EditesOnDataChanges=true}}">
    </TextBox>

    <Button Content="Speichern" Height="23" HorizontalAlignment="Left" Margin="102,110,0,0" VerticalAlignment="Top" Width="75" Command="{Binding SaveCommand}"/>
    <Button Content="Abbrechen" Height="23" HorizontalAlignment="Left" Margin="197,110,0,0" VerticalAlignment="Top" Width="75" Command="{Binding CancelCommand}"/>
</Grid>

视图模型

public class MenuangebotVM : DetailVM, IContains
{
    #region private Values
    private Menuangebot myOriginal = new Menuangebot();
    private Menuangebot myValue = new Menuangebot();
    #endregion // private Values

    #region Properties

    #region Detail Properties
    public int Id { get { return myOriginal.Id; } }
    public bool? Result { get; private set; }
    public string Beschreibung { get { return "Einrichtung"; } }

    [Required]
    [RegularExpression(@"^[0-9a-zA-ZäöüßÄÖÜß''-'\s]{2,40}$")]
    [Default(myOriginal.Name)] //<-- added
    public string NameD
    {
        get { return myValue.Name; }
        set
        {
            myValue.Name = value;
            RaisePropertyChanged(() => Reg(() => NameD));
        }
    }

    [Required]
    [RegularExpression(@"^[0-9a-zA-ZäöüßÄÖÜß''-'\s]{2,25}$")]
    [Default(myOriginal.Anzeigetext)] //<-- added
    public string AnzeigetextD
    {
        get { return myValue.Anzeigetext; }
        set
        {
            myValue.Anzeigetext = value;
            RaisePropertyChanged(() => Reg(() => AnzeigetextD));
        }
    }

    [Required]
    [Default(myOriginal.Preis)] //<-- added
    public decimal PreisD
    {
        get { return myValue.Preis; }
        set
        {
            myValue.Preis = value;
            RaisePropertyChanged(() => Reg(() => PreisD));
        }
    }    
    #endregion //Detail Properties
    #endregion //Properties

// more code

}

【问题讨论】:

    标签: c# wpf user-interface mvvm


    【解决方案1】:
    public class ViewModel:INotifyPropertyChanged
        {
            private string initialText;
            public ViewModel()
            {
                Text = "ABCD";
                initialText = Text;
                DefaultBorder = true;
            }
            private string text;
            public string Text
            {
                get { return text; }
                set { text = value;
                if (value == initialText)
                    DefaultBorder = true;
                else
                    DefaultBorder = false;
                    Notify("Text"); }
            }
    
            private bool defaultBorder;
            public bool DefaultBorder
            {
                get { return defaultBorder; }
                set { defaultBorder = value; Notify("DefaultBorder"); }
            }
    
            private void Notify(string propertyName)
            {
                if(PropertyChanged!=null)
                    PropertyChanged(this,new PropertyChangedEventArgs(propertyName));
            }
    
            public event PropertyChangedEventHandler PropertyChanged;
        }
    
    public class MyConverter : IValueConverter
        {
            public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
            {
                if (value != null && value is bool && !(bool)value)
                    return new SolidColorBrush(Colors.Orange);
                else
                    return new SolidColorBrush(Colors.Navy); //Or default whatever you want
    
            }
    
            public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
            {
                throw new NotImplementedException();
            }
        }
    
        <Window.Resources>
        <local:MyConverter x:Key="MyConverter"/>
    </Window.Resources>
    <Grid>
        <TextBox BorderThickness="4" BorderBrush="{Binding DefaultBorder, Converter={StaticResource MyConverter}}" Text="{Binding Text, UpdateSourceTrigger=PropertyChanged}"/>
    </Grid>
    

    这里我在 ViewModel 中有绑定到 TextBox 的属性 Text ,并且在开始时我保留了初始 Text 值。然后每当用户输入我在 Text 属性的设置器中比较它并相应地设置 Bool 属性时,这个 bool 属性将指定使用转换器绑定哪种颜色。忽略小问题希望你能有所了解。

    【讨论】:

    • mhh 所以我只在我的文本属性中移动问题(自我重复)并且没有否认它们。我认为可能有一个ValidationAttribute 解决方案
    • 您的意思是要将橙色边框画笔显示为文本中的错误。
    • 如果是实际的Text != startText
    【解决方案2】:

    您可以将其转换为自定义控件/UserControl 并添加 IsDirtyDependencyProperty 和 IsDirtyColorDependencyProperty(或附加的依赖项属性)。这样你就可以用这个替换所有的文本框,而不必一遍又一遍地重复代码。

    【讨论】:

      【解决方案3】:

      巧合的是,我刚刚遇到了一个与您的问题几乎相同的问题,我通过将TextBox 包装在Border 中解决了它。此外,这也解决了在 Windows 8 机器上无法更改 BorderBrush 颜色的问题。

      因此我会推荐这种方法。代码非常简单,只需将文本框添加到 Border 并更改边框的 BorderBrush 属性即可。

      【讨论】:

      • 所以你像 TYY 建议的那样创建了一个服装控件?
      • @WiiMaxx - 不,我的方法比这简单得多。从字面上看,只需将您的 TextBox 包装在 Border 控件中。无需任何自定义控件。
      • 你能给我举个例子吗,因为我现在看不到你的解决方案的改进
      • 我目前无法访问任何代码,但您可以在此处阅读有关 Border 控件及其属性的信息:msdn.microsoft.com/en-us/library/… 您可能会注意到属性 Child 是非常适合存储您的文本框。
      【解决方案4】:

      您可以为此使用附加行为

      public static class TextChangedAttachedBehavior
      {
          public static bool GetChanged(DependencyObject obj)
          {
              return (bool)obj.GetValue(ChangedProperty);
          }
      
          public static void SetChanged(DependencyObject obj, string value)
          {
              obj.SetValue(ChangedProperty, value);
          }
      
          public static readonly DependencyProperty ChangedProperty =
              DependencyProperty.RegisterAttached("Changed", typeof(bool),
              typeof(TextChangedAttachedBehavior), new PropertyMetadata(false, HookupBehavior));
      
          private static void HookupBehavior(DependencyObject d, DependencyPropertyChangedEventArgs e)
          {
              var textBox = d as TextBox;
              if (textBox == null) 
                  return;
              textBox.TextChanged += TextBoxOnTextChanged;
          }
      
          private static void TextBoxOnTextChanged(object sender, TextChangedEventArgs args)
          {
              var textBox = sender as TextBox;
              if (textBox == null)
                  return;
              textBox.BorderBrush = new SolidColorBrush(Colors.Orange);
          }
      }
      

      比在 xaml 中

      <TextBox Text="{Binding myText1, UpdateSourceTrigger=PropertyChanged, ValidatesOnDataErrors=True}" TextChangedAttachedBehavior.Changed = "True" />
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-03-27
        相关资源
        最近更新 更多