понедельник, 27 октября 2014 г.

Создание объектов из XAML

Возник на форумах MSDN вопрос, как привязать команду к контекстному меню. У автора небольшое недопонимание Binding-а, но не суть. Предлагаю посмотреть, как можно создавать объекты их XAML и использовать их, например, как источники в Binding.
Создаем новое WPF приложение и добавляем в него нашу команду:

public class SimpleCommand : ICommand
{
    public void Execute(object parameter)
    {
        MessageBox.Show("Привет");
    }
 
    public bool CanExecute(object parameter)
    {
        return true;
    }
 
    public event EventHandler CanExecuteChanged;
}

На форму приложения подключаем пространство имен:

xmlns:local="clr-namespace:WpfApplication2"

И в ресурсах создаем наш объект на основе SimpleCommand:

<Window.Resources>
    <ObjectDataProvider ObjectType="{x:Type local:SimpleCommand}" x:Key="comm" />

Ну а теперь добавляем в ресурсы наше контекстное меню:

<ContextMenu x:Key="Hello">
    <MenuItem Header="Hello" Command="{Binding Source={StaticResource comm}}" />
</ContextMenu>

Я указал наш созданный класс в качестве источника, Path не указываю, т.к. сам объект помещенный в Source нас и интересует. Но можно и классически, объект источника поместить в DataContext:

<MenuItem Header="Hello" Command="{Binding }" DataContext="{StaticResource comm}" />

Для демо, я кинул на форму TextBox и указал у него наше контекстное меню:

<TextBox ContextMenu="{StaticResource Hello}" />

Запускаем:
Ну и два усовершенствования. По-первых, при создании объектов их XAML мы можем передавать в конструктор параметры. Переписываем нашу команду:

public class SimpleCommand : ICommand
{
    private string _message;
 
    public SimpleCommand(string p_message)
    {
        _message = p_message;
    }
 
    public void Execute(object parameter)
    {
        MessageBox.Show(_message);
    }
 
    public bool CanExecute(object parameter)
    {
        return true;
    }
 
    public event EventHandler CanExecuteChanged;
}

Подключаем пространство имен System:

xmlns:system="clr-namespace:System;assembly=mscorlib"

И создаем не один объект, а два:

<ObjectDataProvider ObjectType="{x:Type local:SimpleCommand}" x:Key="commHello">
    <ObjectDataProvider.ConstructorParameters>
        <system:String>Hello</system:String>
    </ObjectDataProvider.ConstructorParameters>
</ObjectDataProvider>
<ObjectDataProvider ObjectType="{x:Type local:SimpleCommand}" x:Key="commBye">
    <ObjectDataProvider.ConstructorParameters>
        <system:String>Bye</system:String>
    </ObjectDataProvider.ConstructorParameters>
</ObjectDataProvider>

Правим контекстное меню (я Key поменял, надо и в TextBox тоже поправить):

<ContextMenu x:Key="Menu">
    <MenuItem Header="Hello" Command="{Binding }" DataContext="{StaticResource commHello}" />
    <MenuItem Header="Bye" Command="{Binding }" DataContext="{StaticResource commBye}" />
</ContextMenu>

Во-вторых, если бы я решал эту задачу, то я бы добавил в проект еще один класс:

class ContextMenuViewModel
{
    public ICommand Hello { get; set; }
 
    public ICommand Bye { get; set; }
 
    public ContextMenuViewModel()
    {
        Hello = new SimpleCommand("Hello");
        Bye = new SimpleCommand("Bye");
    }
}

Ну и окно переписал бы так:

<ObjectDataProvider ObjectType="{x:Type local:ContextMenuViewModel}" x:Key="contextMenuViewModel" />
<ContextMenu x:Key="Menu" DataContext="{StaticResource contextMenuViewModel}">
    <MenuItem Header="Hello" Command="{Binding Hello}" />
    <MenuItem Header="Bye" Command="{Binding Bye}" />
</ContextMenu>

Все.

Комментариев нет:

Отправить комментарий