WPF自定义复合用户控件

时间:2016-11-10 10:39:25

标签: c# wpf xaml mvvm

首先,我是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));
    }
}

这是调用View和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.NameVM.Value属性绑定到UC.ParamName和{{ 1}}。相反,UC.ValueParameterList部分加载。MainViewModel.ParametersUC.Name绑定良好,但UC.ParamName未绑定到VM.Parameters {1}}(UC.Items是虚拟机,UC.DataContext已明确定义,但VM.Parameters绝望UC.Items。)

你知道我错过了什么吗? (我不是母语人士,如果我的英语伤害了你,请原谅我)

2 个答案:

答案 0 :(得分:0)

我看到你有一个绑定的MainViewModel.Parameters - &gt; ParameterListControl.Items但您可能缺少来自ParameterListControl.Items的绑定 - &gt; ParameterList.Parameters。 (那是假设 ParameterList是ParameterListControl的ViewModel - 您提供了DataContext绑定的代码。)

the accepted answer on this question。 (忽略对Caliburn.Micro的评论 - 同样的解决方案在MVVM Light中适用于我。)

本质上,在ParameterListControl的构造函数中,您在视图的依赖项属性和viewmodel的属性之间创建了一个额外的绑定。

(另外,Dbl在评论中是正确的,当调试绑定问题时,您省略的“不重要”“管道”代码非常重要。)

答案 1 :(得分:0)

我终于找到了:

ParameterListControl的func points(fromInches inches: CGFloat) -> CGFloat { return inches * ppi / UIScreen.main.scale } var ppi: CGFloat { //return device ppi } 依赖项属性是Items。这是来自另一个UC的复制/粘贴错误。我将其更改为IList<string>,现在一切正常:

IEnumerable

我继续研究代码,现在已完成,并且与我之前发布的示例相比真正复合了。如果有人有兴趣查看/使用此代码,您可以在github上找到它。

相关问题