在视图模型和视图之间使用MVVM进行WPF数据上下文绑定 [英] Wpf datacontext binding using MVVM between viewmodel and view

查看:77
本文介绍了在视图模型和视图之间使用MVVM进行WPF数据上下文绑定的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我刚刚开始学习MVVM,这似乎是一个基本问题,但我花了一整天的时间试图弄清楚。

I just started learning MVVM and here is what seems to be basic question but I spent whole day trying to figure it out.

我有一个包含3个解决方案为Model投影一个,为ViewModel投影一个,为View投影一个。该模型包含一个类,该类具有2个属性Text和CheckStatus。

I have a solution that contains 3 projects one for Model, one for ViewModel and one for View. The Model contains a class that has 2 properties Text and CheckStatus.

ViewModel具有一个名为listOfItems的列表,该列表包含三个项目,每个项目都具有模型中的这两个属性。

The ViewModel has a list called listOfItems that has three items, each item has these 2 properties from the Model.

该视图内部有一个listView,其中有一个CheckBox。将CheckBox内容绑定到属性Text的正确方法是什么?

The View has a listView inside it there is a CheckBox. What is the proper way to bind the CheckBox content to the property Text?

这里是模型

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;


namespace TheModel
{
public class CheckBoxListModel : INotifyPropertyChanged
{
    private string text;
    public string Text
    {
        get { return text; }
        set
        {
            text = value;
            RaiseChanged("Text");
        }
    }

    private bool checkStatus;
    public bool CheckStatus
    {
        get { return checkStatus; }
        set
        {
            checkStatus = value;
            RaiseChanged("CheckStatus");
        }
    }

    private void RaiseChanged(string propName)
    {
        if (PropertyChanged != null)
        {
            PropertyChanged(this, new PropertyChangedEventArgs(propName));
        }
    }

    public event PropertyChangedEventHandler PropertyChanged;
   }
}

这里是视图模型

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Collections.ObjectModel;
using TheModel;

namespace TheViewModel
{
public class TheViewModel
{
    public List<CheckBoxListModel> ListOfItems { get; set; }

    public TheViewModelClass()
    {
        ListOfItems = new List<CheckBoxListModel>
        {
        new CheckBoxListModel
        {
            CheckStatus = false,
            Text = "Item 1",
        },
        new CheckBoxListModel
        {
            CheckStatus = false,
            Text = "Item 2",
        },
        new CheckBoxListModel
        {
            CheckStatus = false,
            Text = "Item 3",
        }
    };
    }

    public static implicit operator List<object>(TheViewModelClass v)
    {
        throw new NotImplementedException();
    }
   }
}

这是View XAML

and here is the View XAML

 <UserControl
 xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
 xmlns:ctrl="clr-namespace:TheView.Managers" xmlns:TheViewModel="clr-
 namespace:TheViewModel;assembly=TheViewModel" 
 x:Class="TheView.Styles.ListViewDatabaseStyle">

<UserControl.DataContext>
    <TheViewModel:TheViewModelClass/>
</UserControl.DataContext>

<Grid>
    <Grid.RowDefinitions>
        <RowDefinition Height="*"/>
        <RowDefinition Height="100"/>
    </Grid.RowDefinitions>
    <Button Content="Continue" Style="{StaticResource ButtonStyle}" 
          Margin="1104,27,40,40"/>
    <ListView x:Name="listView1" SelectionMode="Multiple" 
              Style="{StaticResource ListViewStyle}" Margin="10,55,10,10"
              ctrl:ListViewLayoutManager.Enabled="true" ItemsSource="
          {Binding TheViewModelClass}" >
        <ListView.View>
            <GridView>
                <GridViewColumn Header="Competency Items" 
                  ctrl:ProportionalColumn.Width="1100"/>
            </GridView>
        </ListView.View>
        <ListView.ItemContainerStyle >
            <Style TargetType="{x:Type ListViewItem}">
                <Setter Property="IsSelected" Value="{Binding 
                             CheckedStatus}"/>
                <Setter Property="HorizontalContentAlignment" 
                              Value="Stretch"/>
            </Style>
        </ListView.ItemContainerStyle>
        <ListView.ItemTemplate>
            <DataTemplate>
                <CheckBox  
                     Click="CheckBox_Click"
                     Content="{Binding Path=TheViewModelClass.Text}"
                     IsChecked="{Binding 
                     Path=TheViewModelClass.CheckedStatus}" />
            </DataTemplate>
        </ListView.ItemTemplate>
    </ListView>
</Grid>
</UserControl>

这是代码背后的视图,我知道我不应该在这里放东西,但是那部分应该放在哪里

Here is the View behind code, I know I shouldn't have something here but where should that part go?

using System.Windows;
using System.Windows.Controls;
using System.Collections.Generic;
using System.ComponentModel;
using System.Windows.Controls.Primitives;
using System.Windows.Media;
using System;
using System.Text;
using TheViewModel;

namespace TheView.Styles
{
public partial class ListViewDatabaseStyle : UserControl
{
    public ListViewDatabaseStyle()
    {
        InitializeComponent();
    }

    public List<string> selectedNames = new List<string>();
    private void CheckBox_Click(object sender, RoutedEventArgs e)
    {
        var ChkBox = sender as CheckBox;
        var item = ChkBox.Content;
        bool isChecked = ChkBox.IsChecked.HasValue ? ChkBox.IsChecked.Value 
         : false;
        if (isChecked)
            selectedNames.Add(item.ToString());
        else
            selectedNames.Remove(item.ToString());
    }
  }
 }


推荐答案

首先。设置项目的依存关系。 ViewModel必须具有访问模型。 (视图和模型项目不必引用其他项目。)如果您是我,我将做一个启动项目,将控件转移到ViewModel。
此启动项目应为WPF,其他所有项目应为类库,但不要忘记添加对项目的必需引用(例如,用于查看项目的system.xaml用于创建用户控件。)

First of all. Set dependencies of projects. ViewModel must have access Model. (View and Model projects do not have to reference to other projects.) If i were you i would make a StartUp Project to transfer the control to ViewModel. This "StartUp" project should be WPF, all of others should be "class library" but don't forget to add the required references to projects (For example the system.xaml for your view project to create usercontrols.)

项目依赖项:
-StartUp-> ViewModel;
(-ViewModel->视图;或通过DI避免它)
-ViewModel->模型;
(我应该为接口做另一个项目,只是这只是我的变态。)

Projects dependencies: - StartUp --> ViewModel; (- ViewModel --> View; or avoid this with DI) - ViewModel --> Model; (I should make another project for interfaces just this is just my perversions.)

启动项目
现在启动(WPF)项目中的文件应包含在(app.xaml.cs)中:

StartUp Project: Now in your startup (WPF) project should contains in (app.xaml.cs):

protected override void OnStartup(StartupEventArgs e)
{
    // delete the startupuri tag from your app.xaml
    base.OnStartup(e);
    //this MainViewModel from your ViewModel project
    MainWindow = new MainWindow(new MainViewModel());
} 

启动wpf项目中唯一的一件事(窗口)(显示您的

The only one thing (Window) in your startup wpf project (to display your UserControls).

MainWindow.xaml内容:

MainWindow.xaml content:

<Window x:Class="StartUp.MainWindow"
            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
            Title="MainWindow" WindowState="Maximized" WindowStyle="None" AllowsTransparency="True">
        <ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center" Content="{Binding Control}"/>
</Window>

(和xaml.cs)

  public partial class MainWindow : Window
    {
        public MainWindow(INotifyPropertyChanged ViewModel)
        {
            InitializeComponent();
            this.DataContext = ViewModel;
            this.Show();
        }
    }

这就是您所有的Startup WPF项目。
这样,我们将控件交给了您的ViewModel项目。

And Thats all your StartUp WPF project. In this way we gave the control to your ViewModel project.

(好吧,这只是一个额外的功能,但是我应该使用 ViewService来处理我的UserControls)

(Okay, its just an extra, but i should make a "ViewService" to handle my UserControls)

找到所有View并将其与ViewModel匹配的接口。

Interface to find all of View and match the View with ViewModel.

public interface IControlView
{
    INotifyPropertyChanged ViewModel { get; set; }
}

我创建了一个单例来存储视图并使其与视图模型匹配。 (您可以跳过这一部分。)我在 Model 项目中定义了该变量。

I created a singleton to store and match my views with my viewmodels. (You can skip this part.) I defined this in Model project.

 public class ViewService<T> where T : IControlView
    {
        private readonly List<WeakReference> cache;

        public delegate void ShowDelegate(T ResultView);
        public event ShowDelegate Show;
        public void ShowControl<Z>(INotifyPropertyChanged ViewModel)
        {
            if (Show != null)
                Show(GetView<Z>(ViewModel));
        }

        #region Singleton

        private static ViewService<T> instance;
        public static ViewService<T> GetContainer
        {
            get
            {
                if (instance == null)
                {
                    instance = new ViewService<T>();
                }
                return instance;
            }
        }

        private ViewService()
        {
            cache = new List<WeakReference>();
            var types = AppDomain.CurrentDomain.GetAssemblies().SelectMany(s => s.GetTypes()).Where(r => typeof(T).IsAssignableFrom(r) && !r.IsInterface && !r.IsAbstract && !r.IsEnum);

            foreach (Type type in types)
            {
                cache.Add(new WeakReference((T)Activator.CreateInstance(type)));
            }
        }

        #endregion

        private T GetView<Z>(INotifyPropertyChanged ViewModel)
        {
            T target = default(T);
            foreach (var wRef in cache)
            {
                if (wRef.IsAlive && wRef.Target.GetType().IsEquivalentTo(typeof(Z)))
                {
                    target = (T)wRef.Target;
                    break;
                }
            }

            if(target==null)
                target = (T)Activator.CreateInstance(typeof(Z));

            if(ViewModel != null)
                target.ViewModel = ViewModel;

            return target;
        }

    }

现在,您获得了服务,从
ViewModel 在主窗口中显示UserControls:

And now you have got a "service" to show your UserControls in the mainwindow from your ViewModel:

public class MainViewModel : INotifyPropertyChanged
    {

        private IControlView _control;
        public IControlView Control
        {
            get
            {
                return _control;
            }
            set
            {
                _control = value;
                OnPropertyChanged();
            }
        }

        public MainViewModel()
        {   //Subscribe for the ViewService event:   
            ViewService<IControlView>.GetContainer.Show += ShowControl;
            // in this way, here is how to set a user control to the window.
            ViewService<IControlView>.GetContainer.ShowControl<ListViewDatabaseStyle>(new TheViewModel(yourDependencyItems));
           //you can call this anywhere in your viewmodel project. For example inside a command too.
        }

        public void ShowControl(IControlView ControlView)
        {
            Control = ControlView;
        }

        //implement INotifyPropertyChanged...
        protected void OnPropertyChanged([CallerMemberName] string name = "propertyName")
        {
           PropertyChangedEventHandler handler = PropertyChanged;
           if (handler != null)
           {
               handler(this, new PropertyChangedEventArgs(name));
           }
        }

           public event PropertyChangedEventHandler PropertyChanged;
    }

如果您不想使用此 ViewService。只需创建一个UserControl实例,将View的DataContext与ViewModel匹配,然后将此视图提供给Control属性即可。
这是带有列表的ViewModel(仍在ViewMoldel项目中。)

If you don't want to use this "ViewService". Just create an UserControl instance, match DataContext of View with your ViewModel and give this view to Control property. Here is your ViewModel with list (still in ViewMoldel project.)

public class TheViewModel
    {
        private readonly ObservableCollection<ISelectable> listOfItems;
        public ObservableCollection<ISelectable> ListOfItems 
        {
            get { return listOfItems; }
        }

        public ICommand SaveCheckedItemsText{
            get{ return new RelayCommand(CollectNamesOfSelectedElements);}
        }

        public IEnumerable<ISelectable> GetSelectedElements
        {
            get { return listOfItems.Where(item=>item.CheckStatus); }
        }

        public TheViewModel(IList<ISelectable> dependencyItems)
        {
            listOfItems= new ObservableCollection<ISelectable>(dependencyItems);
        }

        //here is your list...
        private List<string> selectedNames

        //use this...
        private void CollectNamesOfSelectedElements()
        {
           selectedNames = new List<string>();
           foreach(ISelectable item in GetSelectedElements)
           {
             //you should override the ToString in your model if you want to do this...
             selectedNames.Add(item.ToString());
           }
        }

    }

RelayCommand文章

查看:(保留所有用户控件。)

View: (Keep here all of your usercontrols.)

在UserControl(xaml)中:

In your UserControl (xaml):

<UserControl x:Class="View.ListViewDataStyle"
             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" namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
             mc:Ignorable="d">
<Button Command={Binding SaveCheckedItemsText}/>
<!-- Another content -->
    <ListView ItemsSource="{Binding ListOfItems}">
        <ListView.ItemTemplate>
            <DataTemplate>
                <CheckBox Content="{Binding Text}" IsChecked="{Binding CheckedStatus}" />
            </DataTemplate>
        </ListView.ItemTemplate>
    </ListView>
</UserControl>

带有接口的是xaml.cs代码(用于UserControls):

And with interface here is the xaml.cs code (for UserControls):

public partial class ListViewDatabaseStyle : UserControl, IControlView
    {
        public ListViewDatabaseStyle ()
        {
            InitializeComponent();
        }

        public INotifyPropertyChanged ViewModel
        {
            get
            {
                return (INotifyPropertyChanged)DataContext;
            }
            set
            {
                DataContext = value;
            }
        }
    }

最后一个是模型项目以及您的模型:

And the last one is the Model project with your models:

 public interface ISelectable
    {
        bool CheckStatus { get; set; }
    }

public class CheckBoxListModel : INotifyPropertyChanged, ISelectable
{
    private string text;
    public string Text
    {
        get { return text; }
        set
        {
            text = value;
            RaiseChanged("Text");
        }
    }

    private bool checkStatus;
    public bool CheckStatus
    {
        get { return checkStatus; }
        set
        {
            checkStatus = value;
            RaiseChanged("CheckStatus");
        }
    }

    private void RaiseChanged(string propName)
    {
        if (PropertyChanged != null)
        {
            PropertyChanged(this, new PropertyChangedEventArgs(propName));
        }
    }

    public event PropertyChangedEventHandler PropertyChanged;
   }
}

请原谅我英语语法错误,我希望你能理解我的帖子。

Excuse me for english grammar mistakes, i hope you understood my post.

更新:
使用DI技术。避免从viewmodel引用视图。 DI服务将通过构造函数注入来注入正确的对象。

Update: Use the DI techn. to avoid the reference to view from viewmodel. DI service will inject the correct object with constructor injection.

这篇关于在视图模型和视图之间使用MVVM进行WPF数据上下文绑定的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆