WPF / C#从父视图将ViewModel分配给自定义控件

时间:2017-10-27 17:38:23

标签: c# wpf

我是C#和WPF的新手,我正在努力获取我需要的数据。

我有一组主数据,需要与各种用户控件共享,每个用户控件都有自己的ViewModel。问题是我似乎无法从父XAML将ViewModel分配给控件,然后从自定义控件的XAML中访问该ViewModel。

我将控件绑定到Viewmodel,但是控件中的datacontext不允许我在xaml中访问该模型,或者我可以在用户控件中设置datacontext以便我可以访问其viewmodel,但是我无法绑定到xaml中的viewmodel(因为绑定在本地datacontext中查找,而不是父本)。

我可能会认为这一切都是错的,我见过的大多数示例似乎都在自定义控件xaml中实例化了一个ViewModel,但后来我没有看到你如何让ViewModel引用正确的DataModel(或特定的部分)数据模型)。​​

以下希望能够解释我想要做的事情。

首先,我在DataModel.cs中有我的数据模型

using System;
using System.Collections.Generic;

namespace BindingTest1
{

    public class DataModel
    {
        private List<string>[] _dataLists;
        public List<string>[] DataLists
        {
            get { return _dataLists; }
        }


        public DataModel()
        {

            List<string> list0 = new List<string> { "One", "Two", "Three" };
            List<string> list1 = new List<string> { "Alpha", "Beta", "Gamma" };
            _dataLists = new List<String>[] { list0, list1 };
        }
    }
}

在MainViewModel.cs

namespace BindingTest1
{
    class MainViewModel
    {
        private MyViewModel _myFirstViewModel;
        public MyViewModel MyFirstViewModel
        {
            get { return _myFirstViewModel; }
        }

        private MyViewModel _mySecondViewModel;
        public MyViewModel MySecondModel
        {
            get { return _mySecondViewModel; }
        }

        private DataModel _dataModel;
        public DataModel DataModel
        {
            get { return _dataModel; }
        }

        public MainViewModel()
        {
            _dataModel = new DataModel();
            _myFirstViewModel = new MyViewModel(_dataModel.DataLists[0]);
            _mySecondViewModel = new MyViewModel(_dataModel.DataLists[0]);
        }
    }
}

MainWindow.xaml

<Window x:Class="BindingTest1.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:BindingTest1"
        mc:Ignorable="d"
        xmlns:diag="clr-namespace:System.Diagnostics;assembly=WindowsBase"
        Title="MainWindow" Height="350" Width="525">

    <Window.DataContext>
        <local:MainViewModel/>
    </Window.DataContext>

    <Grid>

        <StackPanel HorizontalAlignment="Stretch" Height="100"  VerticalAlignment="Top" Orientation="Horizontal">

        <!-- These were just to check the data was being set up properly -->    
        <ListBox x:Name="listBox1" HorizontalAlignment="Left" Height="100"  VerticalAlignment="Top" Width="100" ItemsSource="{Binding DataModel.DataLists[0]}"/>
        <ListBox x:Name="listBox2" HorizontalAlignment="Left" Height="100"  VerticalAlignment="Top" Width="100" ItemsSource="{Binding DataModel.DataLists[1]}"/>

        <!-- this is what I want to be able to do -->
        <local:MyView ViewModel="{Binding MyFirstViewModel}"/>
        <local:MyView ViewModel="{Binding MySecondViewModel}"/>

        </StackPanel>

    </Grid>
</Window>

(Codebehind是默认的)

在MyViewModel.cs

using System;
using System.Collections.Generic;

namespace BindingTest1
{
    public class MyViewModel
    {
        private List<string> _dataList;
        public List<string> DataList
        {
            get { return _dataList; }
        }

        public MyViewModel(List<string> list)
        {
            _dataList = new List<String>(list);
            _dataList.Add("Some Local Processing");
        }
    }
}

MyView.xaml

<UserControl x:Class="BindingTest1.MyView"
             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" 
             xmlns:local="clr-namespace:BindingTest1"
             mc:Ignorable="d" 
             d:DesignHeight="100" d:DesignWidth="100">
    <Grid>
        <ListBox x:Name="listBox" HorizontalAlignment="Left" Height="100" VerticalAlignment="Top" Width="100" ItemsSource="{Binding ViewModel.DataList}"/>
    </Grid>
</UserControl>

代码隐藏

using System.Windows;
using System.Windows.Controls;

namespace BindingTest1
{
    /// <summary>
    /// Interaction logic for MyView.xaml
    /// </summary>
    public partial class MyView : UserControl
    {
        public MyViewModel ViewModel
        {
            get { return (MyViewModel)GetValue(ViewModelProperty); }
            set { SetValue(ViewModelProperty, value); }
        }

        public static readonly DependencyProperty ViewModelProperty = DependencyProperty.Register("ViewModel", typeof(MyViewModel), typeof(MyView), 
            new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.None, new PropertyChangedCallback(OnViewModelChanged)));

        public MyView()
        {
            InitializeComponent();
        }

        private static void OnViewModelChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e)
        {
            // Just making sure the right thing is being received
            List<string> dataList = (e.NewValue as MyViewModel).DataList;
            foreach(string line in dataList)
            {
                System.Console.WriteLine(line);
            }
        }
    }
}

2 个答案:

答案 0 :(得分:0)

这是你应该怎么做的。您的视图不需要ViewModel属性。它应该绑定到其DataContext的属性,它将是viewmodel。

视图:

ItemsSource="{Binding DataList}"

窗口:

<Window.Resources>
    <DataTemplate DataType="{x:Type local:MyViewModel}">
        <local:MyView 
            />
    </DataTemplate>
</Window.Resources>

<Grid>

    <StackPanel HorizontalAlignment="Stretch" Height="100"  VerticalAlignment="Top" Orientation="Horizontal">

        <!-- ... -->

        <ContentControl Content="{Binding MyFirstViewModel}"/>
        <ContentControl Content="{Binding MySecondViewModel}"/>
    </StackPanel>

答案 1 :(得分:0)

我认为你不需要依赖属性。

试试这个。

<local:MyView DataContext="{Binding MyFirstViewModel}"/>
<local:MyView DataContext="{Binding MySecondViewModel}"/>

并将DataList绑定到MyView XAML中的ItemsSource。

当您将MyFirstViewModel分配给MyView的DataContext时,内部的绑定将在MyFirstViewModel中查找ItemsSource。