Как повторно использовать экземпляры элементов управления в DataTemplate? - PullRequest
4 голосов
/ 30 марта 2011

У меня есть несколько макетов (разных шаблонов данных) с элементами управления видео.Создание этого видео управления очень долго.Я хочу повторно использовать экземпляры этого элемента управления видео в различных шаблонах данных.

Пример с надписью:

Codebehind и ViewModel:

public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();

            Layout1 = (DataTemplate)this.FindResource("_layout1");
            Layout2 = (DataTemplate)this.FindResource("_layout2");

            DataContext = new ViewModel {Content1 = "Content1", Content2 = "Content2"};
        }

        private void Button_Click(object sender, RoutedEventArgs e)
        {
            _view.ContentTemplate = _view.ContentTemplate == Layout1 ? Layout2 : Layout1;
        }

        DataTemplate Layout1;
        DataTemplate Layout2;
    }

    public class ViewModel
    {
        public string Content1 { get; set; }
        public string Content2 { get; set; }
    }

XAML

<Window Name="_mainForm" x:Class="WpfVideo.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:model="clr-namespace:WpfVideo"
    Title="MainWindow" Height="350" Width="525">
<Window.Resources>
    <DataTemplate x:Key="_layout1" DataType="{x:Type model:ViewModel}">
        <StackPanel>
            <Button Content="{Binding Content1}"/>
            <Button Content="{Binding Content2}"/>
        </StackPanel>   
    </DataTemplate>

    <DataTemplate x:Key="_layout2" DataType="{x:Type model:ViewModel}">
        <StackPanel Orientation="Horizontal">
            <Button Content="{Binding Content1}"/>
            <Button Content="{Binding Content2}"/>
        </StackPanel>
    </DataTemplate>
</Window.Resources>

<StackPanel>
    <Button Click="Button_Click">Change</Button>
    <ContentPresenter Name="_view" Content="{Binding}" ContentTemplate="{StaticResource _layout1}"/>
</StackPanel>

Как повторно использовать кнопки и предотвратить создание новых кнопок при каждом изменении шаблона?

РЕДАКТИРОВАНИЕ: Используется переключение между шаблонами данных _layout1 и _layout2 при нажатии кнопки.Активен только один шаблон.Я не хочу рисовать один экземпляр управления в двух местах.Я хочу предотвратить создание элементов управления в другом шаблоне, когда он активирован (предыдущий деактивирован).Или, может быть, я могу использовать другой подход, без шаблонов данных?Стиль, ресурсы, триггеры?

Ответы [ 2 ]

3 голосов
/ 01 апреля 2011

Пул управления

ControlsPoolControl<T> 
IControlsPool<T>
  • T - тип многоразового контроля.
  • IControlsPool - используется для хранения многоразовые экземпляры управления ключом объект (некоторое свойство viewmodel)
  • ControlsPoolControl - Контейнер, которые восстанавливают внутренний контроль IControlsPool с помощью связанного ключа.

Осуществление

ControlsPoolControl

public class ControlsPoolControl<T> : UserControl where T : UIElement
{
    private readonly Panel _mainPanel;
    private T _innerControl;

    public ControlsPoolControl()
    {
        _mainPanel = new Grid();
        Content = _mainPanel;
    }

    #region Properties

    #region DependencyProperty

    public static readonly DependencyProperty KeyObjectProperty = DependencyProperty.Register("KeyObject", typeof(object), typeof(ControlsPoolControl<T>), new PropertyMetadata(null, KeyObjectChanged));
    public static readonly DependencyProperty PoolProperty = DependencyProperty.Register("Pool", typeof(IControlsPool<T>), typeof(ControlsPoolControl<T>), new PropertyMetadata(null, PoolChanged));

    #endregion

    public object KeyObject
    {
        get { return GetValue(KeyObjectProperty); }
        set { SetValue(KeyObjectProperty, value); }
    }

    public IControlsPool<T> Pool
    {
        get { return (IControlsPool<T>)GetValue(PoolProperty); }
        set { SetValue(PoolProperty, value); }
    }

    protected T InnerControl
    {
        get { return _innerControl; }
        set
        {
            if (_innerControl == value)
                return;

            _innerControl = value;
            OnControlChanged();
        }
    }

    #endregion

    #region Private API

    void Clear()
    {
        _mainPanel.Children.Clear();
    }

    void OnKeyObjectChanged()
    {
        UpdateControl();
    }

    void OnControlChanged()
    {
        VerifyAccess();
        Clear();

        var ctrl = InnerControl;
        if (ctrl != null)
            _mainPanel.Children.Add(ctrl);
    }

    private void UpdateControl()
    {
        if (KeyObject == null || Pool == null)
            InnerControl = null;
        else
            InnerControl = Pool.Get(KeyObject);
    }

    private static void KeyObjectChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        ((ControlsPoolControl<T>)d).OnKeyObjectChanged();
    }

    private static void PoolChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        ((ControlsPoolControl<T>)d).UpdateControl();
    }

    #endregion
}

ControlsPool

public interface IControlsPool<T> where T : UIElement
{
    void Add(object key, T control);
    T Get(object key);
}

public class ControlsPool<T> : IControlsPool<T> where T : UIElement
{
    readonly IDictionary<object, T> _controls = new Dictionary<object, T>();

    public void Add(object key, T control)
    {
        if (key == null) throw new ArgumentNullException("key");

        if (_controls.ContainsKey(key)) return;
        _controls.Add(key, control);
    }

    public T Get(object key)
    {
        if (key == null) throw new ArgumentNullException("key");

        T control = null;
        if (!_controls.TryGetValue(key, out control))
        {
            control = CreateInstance(key);
            _controls.Add(key, control);
        }

        return control;
    }

    protected virtual T CreateInstance(object key)
    {
        return Activator.CreateInstance<T>();
    }
}

Использование

Codebehind

public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();

        ContentControlType = typeof(MyButton);

        Layout1 = (DataTemplate)FindResource("_layout1");
        Layout2 = (DataTemplate)FindResource("_layout2");

        DataContext = new ViewModel { Content1 = "Content1", Content2 = "Content2" };
    }

    public Type ContentControlType { get; set; }

    private void Button_Click(object sender, RoutedEventArgs e)
    {
        _view.ContentTemplate = _view.ContentTemplate == Layout1 ? Layout2 : Layout1;
    }

    DataTemplate Layout1;
    DataTemplate Layout2;
}

public class ViewModel
{
    public string Content1 { get; set; }
    public string Content2 { get; set; }
}

public class ButtonsPool : ControlsPool<MyButton> { }

public class ButtonPoolControl : ControlsPoolControl<MyButton>
{
}

public class MyButton : Button
{
    static int _counter = 0;

    public MyButton()
    {
        _counter++;
    }
}

XAML Чтобы инициализировать повторно используемый элемент управления, вы должны использовать стиль.

<Window Name="_mainForm" x:Class="WpfVideo.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:model="clr-namespace:WpfVideo" 
    Title="MainWindow" Height="350" Width="525">
<Window.Resources>

    <model:ButtonsPool x:Key="_buttonsPool"/>

    <DataTemplate x:Key="_layout1" DataType="{x:Type model:ViewModel}">
        <StackPanel>
            <model:ButtonPoolControl KeyObject="{Binding Content1}" Pool="{StaticResource _buttonsPool}">
                <model:ButtonPoolControl.Resources>
                    <Style TargetType="model:MyButton">
                        <Setter Property="Content" Value="{Binding Content1}"/>
                    </Style>
                </model:ButtonPoolControl.Resources>
            </model:ButtonPoolControl>
            <model:ButtonPoolControl KeyObject="{Binding Content2}" Pool="{StaticResource _buttonsPool}">
                <model:ButtonPoolControl.Resources>
                    <Style TargetType="model:MyButton">
                        <Setter Property="Content" Value="{Binding Content2}"/>
                    </Style>
                </model:ButtonPoolControl.Resources>
            </model:ButtonPoolControl>
        </StackPanel>
    </DataTemplate>

    <DataTemplate x:Key="_layout2" DataType="{x:Type model:ViewModel}">
        <StackPanel Orientation="Horizontal">
            <model:ButtonPoolControl Pool="{StaticResource _buttonsPool}" KeyObject="{Binding Content1}">
                <model:ButtonPoolControl.Resources>
                    <Style TargetType="model:MyButton">
                        <Setter Property="Content" Value="{Binding Content1}"/>
                    </Style>
                </model:ButtonPoolControl.Resources>
            </model:ButtonPoolControl>
            <model:ButtonPoolControl Pool="{StaticResource _buttonsPool}" KeyObject="{Binding Content2}">
                <model:ButtonPoolControl.Resources>
                    <Style TargetType="model:MyButton">
                        <Setter Property="Content" Value="{Binding Content2}"/>
                    </Style>
                </model:ButtonPoolControl.Resources>
            </model:ButtonPoolControl>
        </StackPanel>
    </DataTemplate>
</Window.Resources>

<StackPanel>
    <Button Click="Button_Click">Change</Button>
    <ContentPresenter Name="_view" Content="{Binding}" ContentTemplate="{StaticResource _layout1}"/>
</StackPanel>

0 голосов
/ 30 марта 2011

Вы не можете. FrameworkElement и FrameworkContentElement экземпляры могут быть добавлены только к одной точке логического дерева. Если вы попытаетесь добавить один и тот же экземпляр такого объекта в два разных места, вы получите исключение:

Элемент уже имеет логического родителя. Это должно быть отделено от старого родитель, прежде чем он присоединен к новому один.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...