Как соединить событие TextBox TextChanged и команду по порядку использовать шаблон MVVM в Silverlight

Недавно я понял, что шаблон MVVM настолько полезен для приложения Silverlight и изучал, как применить его в моем проекте.

Кстати, Как подключить событие textChanged текстового поля с помощью команды? Для Button есть свойство Command, однако Textbox не имеет свойства commapd. Если у Controls нет свойства команды, как объединить ICommand и событие Control?

Я получил следующий код xaml

<UserControl.Resources>
        <vm:CustomerViewModel x:Key="customerVM"/>    
    </UserControl.Resources>

    <Grid x:Name="LayoutRoot" 
          Background="White" 
          DataContext="{Binding Path=Customers, Source={StaticResource customerVM}, Mode=TwoWay}" >

        <StackPanel>
            <StackPanel Orientation="Horizontal"
                        Width="300"
                        HorizontalAlignment="Center">
                <TextBox x:Name="tbName" 
                         Width="50" 
                         Margin="10"/>
                <Button Width="30" 
                        Margin="10" 
                        Content="Find"
                        Command="{Binding Path=GetCustomersByNameCommand, Source={StaticResource customerVM}}"
                        CommandParameter="{Binding Path=Text, ElementName=tbName}"/>
            </StackPanel>
            <sdk:DataGrid ItemsSource="{Binding Path=DataContext, ElementName=LayoutRoot}"
                          AutoGenerateColumns="True"
                          Width="300"
                          Height="300"/>
        </StackPanel>
    </Grid>

Я пытаюсь сделать следующее: если пользователь введет какой-то текст в текстовое поле, данные будут отображаться в решетка данных вместо нажатия кнопки. Я знаю, что встроенный элемент управления автозаполнением встроен. Однако я хочу знать, как вызывать свойство Command в классе ViewModel в элементах управления, которые не имеют свойства Command, такого как textbox.

Спасибо

17
задан Shimmy 9 February 2018 в 09:11
поделиться

4 ответа

Для разговора допустим, что вам действительно нужно подключить какое-то произвольное событие к команде, а не напрямую связываться со свойством в ViewModel (из-за отсутствия поддержки в контроль или фреймворк, дефект и т. д.) Это можно сделать в программном коде. Вопреки некоторым заблуждениям, MVVM не исключает программной поддержки. Просто важно помнить, что логика в программном коде не должна пересекать уровни - она ​​должна быть связана непосредственно с пользовательским интерфейсом и конкретной используемой технологией пользовательского интерфейса. (Обратите внимание, однако, что помещение 95% вашей работы в файл разметки может сделать несколько неинтуитивно понятным наличие некоторых функций в выделенном коде, поэтому один или два комментария в разметке об этой одноразовой реализации выделенного кода могут сделать вещи легче в будущем для себя или других.)

Обычно привязка команды в программном коде состоит из двух частей. Во-первых, вы должны отреагировать на событие. Во-вторых, вы (возможно) захотите привязать к свойству CanExecute команды.

// Execute the command from the codebehind
private void HandleTheEvent(Object sender, EventArgs e)
{
    var viewModel = DataContext as ViewModel;
    if (viewModel != null)
    {
        var command = viewModel.SomeCommand;
        command.Execute(null);
    }
}

// Listen for the command's CanExecuteChanged event
// Remember to call this (and unhook events as well) whenever the ViewModel instance changes
private void ListenToCommandEvent()
{
    var viewModel = DataContext as ViewModel;
    if (viewModel != null)
    {
        var command = viewModel.SomeCommand;
        command.CanExecuteChanged += (o, e) => EnableOrDisableControl(command.CanExecute(null));
    }
}
3
ответ дан 30 November 2019 в 10:13
поделиться

Почему бы просто не привязать свойство Text к свойству в вашей модели представления? Таким образом вы получите уведомление об изменении, а также получите новое значение:

public string MyData
{
    get { return this.myData; }
    set
    {
        if (this.myData != value)
        {
            this.myData = value;
            this.OnPropertyChanged(() => this.MyData);
        }
    }
}

XAML:

<TextBox Text="{Binding MyData}"/>
23
ответ дан 30 November 2019 в 10:13
поделиться

Вот самый простой способ. Привяжите свое текстовое поле к свойству в модели представления, как описано выше. Затем просто добавьте событие кода программной части (да, я говорю о коде программной части с MVVM, это не конец света) в текстовое поле. Добавьте событие TextChanged, а затем просто обновите привязку.

В целом, у вас будет что-то вроде этого для модели представления:

public class MyViewModel 
{
    private string _myText;

    public string MyText 
    {
        get { return _myText; }
        set 
        {
            _myText = value;
            RaisePropertyChanged("MyText"); // this needs to be implemented
            // now do whatever grid refresh/etc
        }
    }
}

В вашем XAML у вас будет следующее:

<TextBox Text="{Binding MyText,Mode=TwoWay}" TextChanged="TextBox_TextChanged"/>

Наконец, в коде позади просто сделайте следующее:

public void TextBox_TextChanged(object sender, TextChangedEventArgs e)
{
   var binding = ((TextBox)sender).GetBindingExpression(TextBox.TextProperty);
   binding.UpdateSource();
}

Это вызовет ваше свойство для обновления в любое время, когда текст изменяется. }

20
ответ дан 30 November 2019 в 10:13
поделиться

Вы должны использовать Behavior для выполнения команды:

public class CommandBehavior : TriggerAction<FrameworkElement>
{
    public static readonly DependencyProperty CommandBindingProperty = DependencyProperty.Register(
        "CommandBinding",
        typeof(string),
        typeof(CommandBehavior),
        null);

    public string CommandBinding
    {
        get { return (string)GetValue(CommandBindingProperty); }
        set { SetValue(CommandBindingProperty, value); }
    }

    private ICommand _action;

    protected override void OnAttached()
    {
        DataContextChangedHandler.Bind(AssociatedObject, _ProcessCommand);
    }

    private void _ProcessCommand(FrameworkElement obj)
    {
        if (AssociatedObject != null)
        {

            var dataContext = AssociatedObject.DataContext;

            if (dataContext != null)
            {
                var property = dataContext.GetType().GetProperty(CommandBinding);
                if (property != null)
                {
                    var value = property.GetValue(dataContext, null);
                    if (value != null && value is ICommand)
                    {
                        _action = value as ICommand;
                        if (AssociatedObject is Control)
                        {
                            var associatedControl = AssociatedObject as Control;
                            associatedControl.IsEnabled = _action.CanExecute(null);
                            _action.CanExecuteChanged +=
                                (o, e) => associatedControl.IsEnabled = _action.CanExecute(null);
                        }

                    }
                }
            }
        }
    }

    protected override void Invoke(object parameter)
    {
        if (_action != null && _action.CanExecute(parameter))
        {
            _action.Execute(parameter);
        }
    }
}

public static class DataContextChangedHandler
{
    private const string INTERNAL_CONTEXT = "InternalDataContext";
    private const string CONTEXT_CHANGED = "DataContextChanged";

    public static readonly DependencyProperty InternalDataContextProperty =
        DependencyProperty.Register(INTERNAL_CONTEXT,
                                    typeof(Object),
                                    typeof(FrameworkElement),
                                    new PropertyMetadata(_DataContextChanged));

    public static readonly DependencyProperty DataContextChangedProperty =
        DependencyProperty.Register(CONTEXT_CHANGED,
                                    typeof(Action<FrameworkElement>),
                                    typeof(FrameworkElement),
                                    null);


    private static void _DataContextChanged(object sender, DependencyPropertyChangedEventArgs e)
    {
        var control = (FrameworkElement)sender;
        var handler = (Action<FrameworkElement>)control.GetValue(DataContextChangedProperty);
        if (handler != null)
        {
            handler(control);
        }
    }

    public static void Bind(FrameworkElement control, Action<FrameworkElement> dataContextChanged)
    {
        control.SetBinding(InternalDataContextProperty, new Binding());
        control.SetValue(DataContextChangedProperty, dataContextChanged);
    }
}

Теперь вы можете «привязать» вашу команду к xaml:

        <TextBox Text="{Binding SearchText, Mode=TwoWay}" >
            <i:Interaction.Triggers>
                <i:EventTrigger EventName="TextChanged">
                    <utils:CommandBehavior CommandBinding="SearchCommand" />
                </i:EventTrigger>
            </i:Interaction.Triggers>
        </TextBox>

Если вам нужно, вы можете расширить это Behavior дополнительными свойствами, например, если вам нужен отправитель или DataContext другого элемента ..

С уважением, Тамаш

(Я нашел это в сообщении в блоге, но не могу вспомнить его адрес)

0
ответ дан 30 November 2019 в 10:13
поделиться
Другие вопросы по тегам:

Похожие вопросы: