【问题标题】:Managing and Binding Usercontrols管理和绑定用户控件
【发布时间】:2014-04-07 12:10:03
【问题描述】:

我对 WPF 很陌生,现在遇到了一个我找不到任何有效解决方案的场景。 在我的项目中,我有许多视图模型和用户控件。主窗口分为两个,在左侧我根据当前的视图模型显示用户控件,这一切正常。当我想根据用户在左侧选择的内容在右侧显示第二个用户控件时,问题就开始了。用户控件有很多文本框和组合框。我将如何从同序视图模型中绑定这些数据?

//mainwindow.xaml
<Window.resources>
<Datatemplate Datatype={x:Type vm:Viewmodel1}>
<loc:Usercontrol1/> // in the left hand side
</DataTemplate>
<Datatemplate Datatype={x:Type vm:Viewmodel2}>
<loc:Usercontrol2/> // in the lefthand side
</DataTemplate>
</Window.Resources>
...
<Grid Grid.Column="0">
<ContentControl Content={Binding CurrentViewModel}/>
</Grid>
<Grid Grid.Column="1">
<Grid.Resources>
 <Datatemplate Datatype={x:Type vm:Viewmodel1}>
<loc:Usercontrol3 NameDp={Binding Name}/> // in the right hand side
</DataTemplate>
<Datatemplate Datatype={x:Type vm:Viewmodel2}>
<loc:Usercontrol3/> // in the rightthand side
</DataTemplate>
</Grid.Resources>
</Grid>

// Usercontrol3.xaml
<Grid>
<TextBox Text="{Binding Path=NameDp, ElementName=UserControl3}" />
</Grid> 
// UserControl3.cs
public static readonly DependencyProperty NameUCProperty  =DependencyProperty.Register("NameDp", typeof(string), typeof(UserControl3), new FrameworkPropertyMetadata(NamePropertyChanged));
public string NameDp
    {
        get
        {
            return (string)GetValue(NameUCProperty);
        }
        set
        {
            SetValue(NameUCProperty, value);
        }
    }

//ViewModel
Public Name {get;set;}

在每个视图模型中,我从数据库中获取数据,我想根据用户选择将这些数据绑定到右侧的用户控件。我如何绑定这些数据? 这是正确的方法还是我完全错了?

【问题讨论】:

  • 您只需定义一次数据模板,无需多次。从绑定的源开始,在逻辑树上执行搜索以找到与绑定对象的数据类型匹配的第一个数据模板。除此之外,这看起来是正确的。它不工作吗?
  • @Will 正如我提到的,我想先加载左侧,然后当用户选择我想在右侧打开下一个用户控件的内容时。如果我放在一起,我将如何指出应该首先显示的内容。
  • @Will 左侧工作完美。这样甚至可以在右侧加载用户控件。真正的问题是第二组用户控件无法绑定。文本框始终为空白。不明白
  • 嗯,取决于。 “当用户选择某物时”是什么意思?例如,如果 UC1 显示一个项目列表,而您希望 UC2 显示所选项目......这很容易。在 UC1 中,将列表的 SelectedItem 属性绑定到 UserControl 上的公共 DependencyProperty(是的,代码隐藏)。然后,在 Window 中,将第二个 ContentControl 绑定到该公共 DependencyProperty。
  • @Will Yes 左侧总是列表,当用户从列表中选择一个项目时。项目的详细信息应显示在 UC2 中。目前,当用户双击时,我已经在视图模型中实现了一个命令,并将所选项目作为命令参数发送到数据库中搜索以获取更多详细信息。一旦我有了,我想从那里直接绑定到用户控件。如果可能的话。我不确定该怎么做。小的伪代码会很有帮助。

标签: c# wpf mvvm user-controls


【解决方案1】:

这绝对是一种方法。对我来说,我会以这种方式组织事情。我这里有一个原型,可以演示这些东西是如何工作的……

首先,我们的主窗口

<Window x:Class="WpfUserControlsBindingListeningNotMuchHere.MainWindow"
        xmlns:t="clr-namespace:WpfUserControlsBindingListeningNotMuchHere"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:ms="clr-namespace:System;assembly=mscorlib"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow"
        Height="350"
        Width="525">
    <Window.DataContext>
        <t:ViewModel />
    </Window.DataContext>
    <Grid>
        <Grid.ColumnDefinitions>
            <ColumnDefinition />
            <ColumnDefinition />
        </Grid.ColumnDefinitions>
        <t:UserControl1 x:Name="uc1"
                        Items="{Binding Items}"
                        Selected="{Binding Selected, Mode=TwoWay}" />
        <t:UserControl2 Grid.Column="1"
                        DataContext="{Binding Result}" />
    </Grid>
</Window>

注意uc1。它接受一个列表并公开选定的项目。这些都绑定到 ViewModel。属性ItemsSelected 是我在UserControl 上定义的DependencyProperties。如果您愿意,我可以显示代码,但没有它应该是可以理解的。 (如果我调整 DP 定义,Mode=TwoWay 可以被取消。)

在 ViewModel 中,我监听 Selected 的更改并相应地完成我的工作。

class ViewModel : INotifyPropertyChanged
{
    private object _selected;

    private object _result;

    public event PropertyChangedEventHandler PropertyChanged;

    public ObservableCollection<object> Items { get; private set; }

    public object Selected
    {
        get { return this._selected; }
        set
        {
            if (_selected == value)
                return;
            this._selected = value;
            PropertyChanged(this, new PropertyChangedEventArgs("Selected"));
        }
    }

    public object Result
    {
        get { return this._result; }
        set
        {
            if (_result == value )
            return;
            this._result = value;
            PropertyChanged(this, new PropertyChangedEventArgs("Result"));
        }
    }

    public ViewModel()
    {
        Items = new ObservableCollection<object>();
        Items.Add(1);
        Items.Add("hello");
        Items.Add(3.0d);
        PropertyChanged += OnPropertyChanged;
    }

    private void OnPropertyChanged(object sender, PropertyChangedEventArgs e)
    {
        if (e.PropertyName != "Selected")
            return; 

        //DO MASSIVE WORK HER ON BACKGROUND THREAD OR SOMETHING LOL
        Result = "OMG THIS TOOK A LONG TIME, " + Selected.ToString();
    }
}

所以,我只是在观察对 Selected 的更改,此时我会完成我的工作(业务逻辑)并将结果暴露在另一个属性中。然后将其绑定到 UI 中的第二个 UserControl。

正如我所说,UC 代码是微不足道的......

<UserControl x:Class="WpfUserControlsBindingListeningNotMuchHere.UserControl1"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
             x:Name="root">
    <ListBox ItemsSource="{Binding Items, ElementName=root}"
             SelectedItem="{Binding Selected, ElementName=root}" />
</UserControl>

在代码隐藏中(OMG CODEBEHIND LOOK OUT EVIL)

public partial class UserControl1 : UserControl
{
    #region Items
    public static readonly DependencyProperty ItemsProperty =
    DependencyProperty.Register(
        "Items",
        typeof(IEnumerable<object>),
        typeof(UserControl1),
        new UIPropertyMetadata(null));

    public IEnumerable<object> Items
    {
        get { return (IEnumerable<object>)GetValue(ItemsProperty); }
        set { SetValue(ItemsProperty, value); }
    }
    #endregion

    #region Selected
    public static readonly DependencyProperty SelectedProperty =
    DependencyProperty.Register(
        "Selected",
        typeof(object),
        typeof(UserControl1),
        new UIPropertyMetadata(null));

    public object Selected
    {
        get { return (object)GetValue(SelectedProperty); }
        set { SetValue(SelectedProperty, value); }
    }
    #endregion

    public UserControl1()
    {
        InitializeComponent();
    }
}

<UserControl x:Class="WpfUserControlsBindingListeningNotMuchHere.UserControl2"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
             mc:Ignorable="d" 
             d:DesignHeight="300" d:DesignWidth="300">
    <TextBlock Text="{Binding}" />
</UserControl>

这个没有代码隐藏。

【讨论】:

  • 我会试试这个,如果你能把用户控件代码也放上去就太好了。
  • 我试了一整天,但我的要求它不起作用。该列表始​​终具有约束力,但第二组不具有约束力。我的第二组用户控件不仅仅是一个文本框,而是一个包含许多标签和组合框等的表单
  • @user1767798:注意第二个用户控件。 “选定”项绑定到此控件的 DataContext。在此用户控件中,各个表单控件应绑定到此对象的属性。这就像绑定到窗口中 ViewModel 的属性一样。不知道你为什么会遇到问题,因为它是微不足道的......
  • @感谢您的时间和建议,我可以看到我的 viewmodel 属性正在获取数据但它没有显示。但无论如何,我通过删除用户控件并将它们放入资源字典并根据当前视图模型显示来解决这个问题。它工作得很好。
猜你喜欢
  • 2020-02-09
  • 1970-01-01
  • 2011-10-02
  • 2012-06-20
  • 1970-01-01
  • 2016-01-17
  • 2010-12-20
  • 1970-01-01
相关资源
最近更新 更多