2016-11-10 5 views
1

Прежде всего, я начинающий WPF! Мой подход потенциально не является правильным способом делать то, что я хочу, поэтому не стесняйтесь сказать мне, если это так. То, что я хочу сделать, - это составной пользовательский элемент управления в WPF с использованием MVVM.Пользовательские пользовательские элементы управления WPF

Некоторые классы будут делать лучшее представление, чем я, вот мой взгляд модели:

interface IParameter : INotifyPropertyChanged 
{ 
    string Name { get; set;} 
    string Value { get; set;} 
} 

class TextParameter : ViewModelBase, IParameter 
{ 
    private string _value; 

    public string Name { get; private set; } 

    public string Value 
    { 
     get { return _value; } 
     set 
     { 
      _value = value; 
      RaisePropertyChanged(); 
     } 
    } 

    public TextParameter (string name) 
    { 
     this.Name = name; 
    } 
} 

class ParameterList : ViewModelBase, IParameter 
{ 
    private string _value; 

    public string Name { get; private set; } 

    public string Value 
    { 
     get { return _value; } 
     set 
     { 
      _value = value; 
      RaisePropertyChanged(); 
     } 
    } 

    ObservableCollection<IParameter> Parameters { get; set; } 

    public ParameterList (string name, IEnumerable<IParameter> parameters = null) 
    { 
     this.Name = name; 
     this.Parameters = new ObservableCollection<IParameter>(parameters ?? new List<IParameter>()); 
    } 
} 

Я использую MVVM Light, так что весь материал PropertyChanged управляется в ViewModelBase. Кроме того, это не исчерпывающий список всех параметров, есть некоторые другие, более сложные, но проблема в этих проблемах.

Вот мои пользовательские элементы управления пользователя:

TextParameterControl.xaml:

<UserControl x:Class="Stuff.TextParameterControl" [..] x:Name="parent"> 
    <StackPanel DataContext="{Binding ElementName=parent}" Orientation="Horizontal"> 
     <TextBlock Text="{Binding Path=ParamName, StringFormat='{}{0}:'}" Width="100"></TextBlock> 
     <TextBox Text="{Binding Path=Value}" Width="100"></TextBox> 
    </StackPanel> 
</UserControl> 

TextParameterControl.xaml.cs:

public class TextParameterControl : UserControl 
{ 
    #region param name 

    public string ParamName 
    { 
     get { return (string)GetValue(ParamNameProperty); } 
     set { SetValue(ParamNameProperty, value); } 
    } 

    // Using a DependencyProperty as the backing store for ParamName. This enables animation, styling, binding, etc... 
    public static readonly DependencyProperty ParamNameProperty = 
     DependencyProperty.Register("ParamName", typeof(string), typeof(TextParameterControl), new PropertyMetadata(String.Empty)); 

    #endregion 

    #region value 

    public string Value 
    { 
     get { return (string)GetValue(ValueProperty); } 
     set { SetValue(ValueProperty, value); } 
    } 

    // Using a DependencyProperty as the backing store for Value. This enables animation, styling, binding, etc... 
    public static readonly DependencyProperty ValueProperty = 
     DependencyProperty.Register("Value", typeof(string), typeof(TextParameterControl), new PropertyMetadata(String.Empty)); 

    #endregion 

    public TextParameterControl() 
    { 
     InitializeComponent(); 
    } 
} 

ParameterListControl.xaml:

<UserControl x:Class="Stuff.ParameterListControl" [..] x:Name="parent"> 
    <UserControl.Resources> 
     <DataTemplate x:Key="TextParameterTemplate"> 
      <c:TextParameterControl ParamName="{Binding Name}" Value="{Binding Value}"/> 
     </DataTemplate> 
     <DataTemplate x:Key="ParameterListTemplate"> 
      <c:ParameterListControl ParamName="{Binding Name}" Value="{Binding Value}" Items="{Binding Parameters}" /> 
     </DataTemplate> 
     <s:ParameterTemplateSelector x:Key="ParameterSelector" 
      TextParameterTemplate="{StaticResource TextParameterTemplate}" 
      ParameterListTemplate="{StaticResource ParameterListTemplate}"/> 
    </UserControl.Resources> 
    <Expander DataContext="{Binding ElementName=parent}" Header="{Binding Path=ParamName}" IsExpanded="True" ExpandDirection="Down"> 
     <StackPanel> 
      <ItemsControl ItemsSource="{Binding Path=Items}" ItemTemplateSelector="{StaticResource ParameterSelector}"></ItemsControl> 
     </StackPanel> 
    </Expander> 
</UserControl> 

ParameterListControl.xaml.cs:

public partial class ParameterListControl: UserControl 
{ 
    #region param name 

    public string ParamName 
    { 
     get { return (string)GetValue(ParamNameProperty); } 
     set { SetValue(ParamNameProperty, value); } 
    } 

    // Using a DependencyProperty as the backing store for ParamName. This enables animation, styling, binding, etc... 
    public static readonly DependencyProperty ParamNameProperty = 
     DependencyProperty.Register("ParamName", typeof(string), typeof(ParameterListControl), new PropertyMetadata(String.Empty)); 

    #endregion 

    #region value 

    public string Value 
    { 
     get { return (string)GetValue(ValueProperty); } 
     set { SetValue(ValueProperty, value); } 
    } 

    // Using a DependencyProperty as the backing store for Value. This enables animation, styling, binding, etc... 
    public static readonly DependencyProperty ValueProperty = 
     DependencyProperty.Register("Value", typeof(string), typeof(ParameterListControl), new PropertyMetadata(String.Empty)); 

    #endregion 

    #region items 

    public IList<string> Items 
    { 
     get { return (List<string>)GetValue(ItemsProperty); } 
     set { SetValue(ItemsProperty, value); } 
    } 

    public static readonly DependencyProperty ItemsProperty = 
     DependencyProperty.Register("Items", typeof(IList<string>), typeof(ParameterListControl), new PropertyMetadata(new List<string>())); 

    #endregion 

    public ParameterListControl() 
    { 
     InitializeComponent(); 
    } 
} 

Вот мой селектор пользовательского шаблона:

class ParameterTemplateSelector : DataTemplateSelector 
{ 
    public DataTemplate ParameterListTemplate { get; set; } 
    public DataTemplate TextParameterTemplate { get; set; } 

    public override DataTemplate SelectTemplate(object item, DependencyObject container) 
    { 
     if (item is TextParameter) 
     { 
      return this.TextParameterTemplate; 
     } 
     else if (item is ParameterList) 
     { 
      return this.ParameterListTemplate; 
     } 

     throw new Exception(String.Format("This parameter ({0}) is not handled in the application", item.GetType().Name)); 
    } 
} 

А вот вызывающему Просмотр и ViewModel:

ViewModel:

public class MainViewModel : ViewModelBase 
{ 
    public ObservableCollection<IParameter> Parameters { get; set; } 

    public MainViewModel() 
    { 
     this.Parameters = new ObservableCollection<IParameter>(); 
     this.Parameters.Add(new TextParameter("Customer")); 
     // here I am building my complex composite parameter list 
} 

Просмотреть :

<UserControl.Resources> 
    <DataTemplate x:Key="TextParameterTemplate"> 
     <c:TextParameterControl ParamName="{Binding Name}" Value="{Binding Value}"/> 
    </DataTemplate> 
    <DataTemplate x:Key="ParameterListTemplate"> 
     <c:ParameterListControl ParamName="{Binding Name}" Value="{Binding Value}" Items="{Binding Parameters}" /> 
    </DataTemplate> 

    <s:ParameterTemplateSelector x:Key="ParameterSelector" 
     TextParameterTemplate="{StaticResource TextParameterTemplate}" 
     ParameterListTemplate="{StaticResource ParameterListTemplate}"/> 
</UserControl.Resources> 

<ItemsControl ItemsSource="{Binding Parameters}" ItemTemplateSelector="{StaticResource ParameterSelector}"></ItemsControl> 

При запуске приложения, то TextParameter в MainViewModel.Parameters хорошо загружены (VM.Name и VM.Value свойства хорошо переплетены с UC.ParamName и UC.Value. Противоположно, ParameterList в MainViewModel.Parameters частично загружены. UC.Name хорошо переплетены с UC.ParamName но VM.Parameters не переплетены в UC.ItemsUC.DataContext является ВМ, то VM.Parameters хорошо определена, но UC.Items катастрофически null).

У вас есть представление о том, что у меня отсутствует? (я не родной, простите меня, если мой английский у тебя болит)

+0

У вас есть проблемы с привязкой и исключение inotifypropertychanged кода? очень плохая идея. также Параметры не показывают никакого измененного кода/инициализатора. также плохая идея – Dbl

+0

Я добавил весь «сантехнический» код. – fharreau

+0

Мне кажется, что проблема по-прежнему заключается в том, что ваше представление пытается привязать свойство, (Параметры) до его установки, и поскольку вы не размещаете вызовы INotifyPropertyChanged в свой свойстве Parameters, WPF не заметит и ваш привязка не может быть решена. – Dbl

ответ

0

я, наконец, выяснить:

Items свойство зависимостей ParameterListControl было IList<string>. Это была ошибка копирования/вставки из другого UC. Я изменил его IEnumerable и теперь отлично все работает:

public IEnumerable Items 
{ 
    get { return (IEnumerable)GetValue(ItemsProperty); } 
    set { SetValue(ItemsProperty, value); } 
} 

public static readonly DependencyProperty ItemsProperty = 
     DependencyProperty.Register("Items", typeof(IEnumerable), typeof(ParameterListControl), new PropertyMetadata(new List<object>())); 

Я продолжал работать над кодом, и теперь закончен и действительно композит по сравнению с образцом я отправил ранее. Если кто-то заинтересован в просмотре/использовании этого кода, вы можете найти его на github.

+0

рад, что вы поняли это :) – Dbl

0

я вижу, у вас есть обязательные MainViewModel.Parameters -> ParameterListControl.Items но вы, возможно, отсутствует связывание с ParameterListControl.Items -> ParameterList. Параметры. (Это предполагая ParameterList является ViewModel для ParameterListControl. - Вы обеспечиваете код DataContext привязок)

См the accepted answer on this question. (Игнорируйте комментарий на Caliburn.Micro - то же самое решение сработало для меня в MVVM Light.)

По существу, в конструкторе ParameterListControl вы создаете дополнительную привязку между свойством зависимости вида и свойством viewmodel.

(Также, двухместное прямо в комментариях, что при отладке связывания проблем, то «неважным» «сантехника» код, который вы опустили очень важно.)

+0

Я не уверен, чтобы понять ваше первое предложение. Я добавил теперь весь сантехнический код. Вы можете видеть, сохраняется ли ваша гипотеза? Я не понимаю, почему VM.Name правильно привязан к UC.ParamName, когда VM.Paramters не привязан к UC.Items. Я не вижу, где две привязки разные. Я не могу поверить, что мне нужно вручную зарегистрировать привязку в коде позади только для некоторых свойств, а не для других ... – fharreau

Смежные вопросы