【问题标题】:MVVM- How can I bind to a property, which is not a DependancyProperty?MVVM-如何绑定到不是 DependancyProperty 的属性?
【发布时间】:2011-02-04 11:55:00
【问题描述】:

我发现了这个问题MVVM and the TextBox's SelectedText property。但是,我无法让解决方案发挥作用。这是我的非工作代码,我试图在第二个文本框中显示第一个文本框的选定文本。

查看:

SelectedText 和 Text 只是我的 ViewModel 中的字符串属性。

<TextBox Text="{Binding Path=Text, UpdateSourceTrigger=PropertyChanged}"  Height="155" HorizontalAlignment="Left" Margin="68,31,0,0" Name="textBox1" VerticalAlignment="Top" Width="264" AcceptsReturn="True" AcceptsTab="True" local:TextBoxHelper.SelectedText="{Binding SelectedText, UpdateSourceTrigger=PropertyChanged, Mode=OneWayToSource}" />
        <TextBox Text="{Binding SelectedText, Mode=OneWay, UpdateSourceTrigger=PropertyChanged}" Height="154" HorizontalAlignment="Left" Margin="82,287,0,0" Name="textBox2" VerticalAlignment="Top" Width="239" />

TextBoxHelper

 public static class TextBoxHelper
{
    #region "Selected Text"
    public static string GetSelectedText(DependencyObject obj)
    {
        return (string)obj.GetValue(SelectedTextProperty);
    }

    public static void SetSelectedText(DependencyObject obj, string value)
    {
        obj.SetValue(SelectedTextProperty, value);
    }

    // Using a DependencyProperty as the backing store for SelectedText.  This enables animation, styling, binding, etc...
    public static readonly DependencyProperty SelectedTextProperty =
        DependencyProperty.RegisterAttached(
            "SelectedText",
            typeof(string),
            typeof(TextBoxHelper),
            new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, SelectedTextChanged));

    private static void SelectedTextChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e)
    {
        TextBox tb = obj as TextBox;
        if (tb != null)
        {
            if (e.OldValue == null && e.NewValue != null)
            {
                tb.SelectionChanged += tb_SelectionChanged;
            }
            else if (e.OldValue != null && e.NewValue == null)
            {
                tb.SelectionChanged -= tb_SelectionChanged;
            }

            string newValue = e.NewValue as string;

            if (newValue != null && newValue != tb.SelectedText)
            {
                tb.SelectedText = newValue as string;
            }
        }
    }

    static void tb_SelectionChanged(object sender, RoutedEventArgs e)
    {
        TextBox tb = sender as TextBox;
        if (tb != null)
        {
            SetSelectedText(tb, tb.SelectedText);
        }
    }
    #endregion

}

我做错了什么?

【问题讨论】:

    标签: wpf data-binding mvvm attached-properties


    【解决方案1】:

    您需要一个普通的 .net 属性包装器用于依赖属性,例如:

    public string SelectedText
    {
       set {SetSelectedText(this, value);}
    ...
    

    运行时不需要它(运行时使用 set/get),但设计器和编译器需要它。

    【讨论】:

    • SelectedText 属性是附加(依赖)属性,而不是“正常”依赖属性。有关概述,请参阅msdn.microsoft.com/en-us/library/ms749011.aspx
    • 好吧,我看原题的时候不是很仔细。但是,您不认为对试图提供帮助的帖子投反对票太过分了吗?
    • 不要对个人投反对票。它只是帮助人们从无用答案中筛选出有用答案的工具。如果您发现自己发布了一个无用的答案并且被否决了,您可以将其删除。
    【解决方案2】:

    为了让 SelectedTextChanged 处理程序触发 SelectedText 属性,必须具有初始值。如果您不将其初始化为某个值(至少为 string.Empty),则此处理程序将永远不会触发,而您也将永远不会注册 tb_SelectionChanged 处理程序。

    【讨论】:

    • 我改成这样:new FrameworkPropertyMetadata(string.Empty, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, SelectedTextChanged));但它仍然不起作用。我误会你在说什么了吗?
    • 当我运行您的示例代码时,我将 ViewModel 上的 SelectedText 属性的值设置为 string.Empty。更改 PropertyMetadata 不会导致更改的处理程序触发。
    • 我尝试将 SelectedText 设置为 string.Empty,但是在第一个文本框中选择的文本仍然没有显示在第二个文本框中。
    【解决方案3】:

    您的绑定尝试将TextBoxText 属性绑定到TextBox当前数据上下文SelectedText 属性。由于您使用的是附加属性,而不是悬挂在数据上下文中的属性,因此您需要在绑定中提供更多信息:

    <TextBox Text="{Binding local:TextBoxHelper.SelectedText, Mode=OneWay, UpdateSourceTrigger=PropertyChanged}" ... />
    

    其中local 已与包含TextBoxHelper 类的CLR 命名空间相关联。

    【讨论】:

    • 当前数据上下文中有一个 SelectedText 属性,它通过以下方式绑定到附加属性:local:TextBoxHelper.SelectedText="{Binding SelectedText, UpdateSourceTrigger=PropertyChanged, Mode=OneWayToSource}"
    【解决方案4】:

    这不起作用的原因是没有引发属性更改回调(因为来自 VM 的绑定值与属性元数据中指定的默认值相同)。但更根本的是,当所选文本设置为 null 时,您的行为将分离。在这种情况下,我倾向于有另一个附加属性,它只是用于启用对所选文本的监视,然后可以绑定 SelectedText 属性。所以,像这样:

    #region IsSelectionMonitored
    public static readonly DependencyProperty IsSelectionMonitoredProperty = DependencyProperty.RegisterAttached(
        "IsSelectionMonitored",
        typeof(bool),
        typeof(PinnedInstrumentsViewModel),
        new FrameworkPropertyMetadata(OnIsSelectionMonitoredChanged));
    
    [AttachedPropertyBrowsableForType(typeof(TextBox))]
    public static bool GetIsSelectionMonitored(TextBox d)
    {
        return (bool)d.GetValue(IsSelectionMonitoredProperty);
    }
    
    public static void SetIsSelectionMonitored(TextBox d, bool value)
    {
        d.SetValue(IsSelectionMonitoredProperty, value);
    }
    
    private static void OnIsSelectionMonitoredChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e)
    {
        TextBox tb = obj as TextBox;
        if (tb != null)
        {
            if ((bool)e.NewValue)
            {
                tb.SelectionChanged += tb_SelectionChanged;
            }
            else
            {
                tb.SelectionChanged -= tb_SelectionChanged;
            }
    
            SetSelectedText(tb, tb.SelectedText);
        }
    }
    #endregion
    
    #region "Selected Text"
    public static string GetSelectedText(DependencyObject obj)
    {
        return (string)obj.GetValue(SelectedTextProperty);
    }
    
    public static void SetSelectedText(DependencyObject obj, string value)
    {
        obj.SetValue(SelectedTextProperty, value);
    }
    
    // Using a DependencyProperty as the backing store for SelectedText.  This enables animation, styling, binding, etc...
    public static readonly DependencyProperty SelectedTextProperty =
        DependencyProperty.RegisterAttached(
            "SelectedText",
            typeof(string),
            typeof(TextBoxHelper),
            new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, SelectedTextChanged));
    
    private static void SelectedTextChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e)
    {
        TextBox tb = obj as TextBox;
        if (tb != null)
        {
            tb.SelectedText = e.NewValue as string;            
        }
    }
    
    static void tb_SelectionChanged(object sender, RoutedEventArgs e)
    {
        TextBox tb = sender as TextBox;
        if (tb != null)
        {
            SetSelectedText(tb, tb.SelectedText);
        }
    }
    #endregion
    

    然后在您的 XAML 中,您必须将该属性添加到您的第一个 TextBox:

    <TextBox ... local:TextBoxHelper.IsSelectionMonitored="True" local:TextBoxHelper.SelectedText="{Binding SelectedText, Mode=OneWayToSource}" />
    

    【讨论】:

      【解决方案5】:

      这适用于我使用 TextBoxHelper 类。如前所述,您需要使用非空值初始化 TextBoxHelper 的 SelectedText 属性。而不是数据绑定到视图上的字符串属性 (SelText),您应该绑定到应该实现 INotifyPropertyChanged 的​​ VM 的字符串属性。

      XAML:

      <Window x:Class="TextSelectDemo.Window1"
          xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
          xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
          xmlns:local="clr-namespace:TextSelectDemo"
          Height="300" Width="300">
          <StackPanel>
              <TextBox local:TextBoxHelper.SelectedText="{Binding Path=SelText, Mode=TwoWay}" />
              <TextBox Text="{Binding Path=SelText}" />
          </StackPanel>
      </Window>
      

      后面的代码:

      using System.ComponentModel;
      using System.Windows;
      
      namespace TextSelectDemo
      {
          public partial class Window1 : Window, INotifyPropertyChanged
          {
              public Window1()
              {
                  InitializeComponent();
      
                  SelText = string.Empty;
      
                  DataContext = this;
              }
      
              private string _selText;
              public string SelText
              {
                  get { return _selText; }
                  set
                  {
                      _selText = value;
                      if (PropertyChanged != null)
                      {
                          PropertyChanged(this, new PropertyChangedEventArgs("SelText"));
                      }
                  }
              }
      
              public event PropertyChangedEventHandler PropertyChanged;
          }
      }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2020-04-19
        • 1970-01-01
        • 2016-05-04
        • 2011-05-20
        • 2014-09-29
        • 2020-04-01
        • 2011-08-23
        相关资源
        最近更新 更多