MVVM и свойство SelectedText TextBox - PullRequest
       43

MVVM и свойство SelectedText TextBox

13 голосов
/ 11 февраля 2010

У меня есть TextBox с ContextMenu в нем. Когда пользователь щелкает правой кнопкой мыши внутри TextBox и выбирает соответствующий MenuItem, я хотел бы захватить SelectedText в моей модели представления. Я не нашел хороший способ сделать это способом "MVVM".

Пока у меня есть заявка, использующая способ MVVM Джоша Смита. Я хочу перевестись в Cinch. Не уверен, что фреймворк Cinch справится с такими проблемами. Мысли

Ответы [ 3 ]

20 голосов
/ 11 февраля 2010

Нет простого способа привязать SelectedText к источнику данных, потому что это не DependencyProperty ... однако довольно просто создать присоединенное свойство, которое вы могли бы связать вместо этого.

Вот базовая реализация:

public static class TextBoxHelper
{

    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);
        }
    }

}

Затем вы можете использовать его в XAML:

<TextBox Text="{Binding Message}" u:TextBoxHelper.SelectedText="{Binding SelectedText}" />
2 голосов
/ 14 февраля 2010

Примеры приложений в WPF Application Framework (WAF) выбрали другой способ решения этой проблемы. Там ViewModel разрешен доступ к View через интерфейс (IView), и поэтому он может запросить текущий SelectedText.

Я считаю, что связывание не должно использоваться в каждом сценарии. Иногда написание нескольких строк кода намного чище, чем использование высокоразвитых вспомогательных классов. Но это только мое мнение: -)

JBE

1 голос
/ 23 августа 2011

Я знаю, что на него ответили и приняли, но я думал, что добавлю свое решение. Я использую Поведение, чтобы соединить модель представления с TextBox. Поведение имеет свойство зависимости (CaretPositionProperty), которое можно связать двумя способами с моделью представления. Внутренне поведение связано с обновлениями в / из TextBox.

public class SetCaretIndexBehavior : Behavior<TextBox>
    {
        public static readonly DependencyProperty CaretPositionProperty;
        private bool _internalChange;

    static SetCaretIndexBehavior()
    {

    CaretPositionProperty = DependencyProperty.Register("CaretPosition", typeof(int), typeof(SetCaretIndexBehavior), new PropertyMetadata(0, OnCaretPositionChanged));
}

public int CaretPosition
{
    get { return Convert.ToInt32(GetValue(CaretPositionProperty)); }
    set { SetValue(CaretPositionProperty, value); }
}

protected override void OnAttached()
{
    base.OnAttached();
    AssociatedObject.KeyUp += OnKeyUp;
}

private static void OnCaretPositionChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
    var behavior = (SetCaretIndexBehavior)d;
    if (!behavior._internalChange)
    {
        behavior.AssociatedObject.CaretIndex = Convert.ToInt32(e.NewValue);
    }
}

    private void OnKeyUp(object sender, KeyEventArgs e)
    {
        _internalChange = true;
        CaretPosition = AssociatedObject.CaretIndex;
        _internalChange = false;
    }
}
...