MVVM - Как я могу привязать к свойству, которое не является DependancyProperty? - PullRequest
1 голос
/ 05 апреля 2010

Я нашел этот вопрос MVVM и свойство SelectedText TextBox . Тем не менее, у меня возникли проблемы с получением решения для работы. Это мой нерабочий код, в котором я пытаюсь отобразить выделенный текст первого текстового поля во втором текстовом поле.

Вид:

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

}

Что я делаю не так?

Ответы [ 5 ]

1 голос
/ 06 апреля 2010

Это работает для меня, используя класс TextBoxHelper.Как уже упоминалось, вам нужно инициализировать свойство SelectedText TextBoxHelper с ненулевым значением.Вместо привязки данных к строковому свойству (SelText) в представлении вы должны привязать строковое свойство вашей виртуальной машины, которое должно реализовать INotifyPropertyChanged.

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;
    }
}
1 голос
/ 06 апреля 2010

Причина, по которой это не работает, заключается в том, что обратный вызов изменения свойства не вызывается (поскольку значение привязки для вашей виртуальной машины совпадает со значением по умолчанию, указанным в метаданных для свойства). Более фундаментально, однако, ваше поведение будет отключаться, когда выбранный текст установлен на ноль. В подобных случаях я склонен иметь другое прикрепленное свойство, которое просто используется для включения мониторинга выделенного текста, а затем свойство 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}" />
1 голос
/ 05 апреля 2010

Чтобы обработчик SelectedTextChanged сработал, свойство SelectedText должно иметь начальное значение. Если вы не инициализируете это каким-либо значением (string.Empty как минимум), этот обработчик никогда не сработает и, в свою очередь, вы никогда не зарегистрируете обработчик tb_SelectionChanged.

0 голосов
/ 05 апреля 2010

Ваша привязка пытается связать свойство Text вашего TextBox со свойством SelectedText текущего * контекста данных . Поскольку вы работаете с присоединенным свойством, а не со свойством, висящим за пределами контекста данных, вам потребуется предоставить больше информации в привязке:

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

Где local было связано с пространством имен CLR, содержащим класс TextBoxHelper.

0 голосов
/ 05 апреля 2010

Вам нужна обычная оболочка свойства .net для свойства зависимости, например:

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

Это не требуется во время выполнения (время выполнения использует set / get), но требуется для разработчика и компилятора.

...