一个视图模型为用户控件和窗口或单独的ViewModels视图、控件、模型、窗口

2023-09-02 01:37:27 作者:古有照妖镜今有卸妆油

我有主窗口 AddEdit 用户控件。在主窗口我使这个AddEdit像<浏览次数:AddEditData /> ,previous此命名空间添加到窗口元素:

I have MainWindow and AddEdit UserControl. Inside MainWindow I render this AddEdit like <Views:AddEditData />, previous this namespace is added to Window element:

xmlns:Views="clr-namespace:MyProject.WPF.Views"

+++++++++++++++ ++++++++++++++++
ListOfData    + + DataDetails  +
              + +              +
   DataOne    + + Name: txtBox1+
   DataTwo    + +              +
   DataThree  + +              +
              + +  Save data   +
+++++++++++++++ ++++++++++++++++

当用户选择的数据在左边(DataTwo为例)我想要显示它的属性(为简单起见仅Name属性)内AddEdit用户控件(DataDetails面板)。

When user selects data on left side (DataTwo for example) I want to display it's properties (for simplicity only Name property) inside AddEdit user control (DataDetails panel).

由于该用户控件从主窗口分开存储的,我应该用同样的MainWindowViewModel和相同的DataContext还是应该创建分离视图模型为AddEdit 用户控件

Since this UserControl is stored separately from the MainWindow should I use same MainWindowViewModel and same datacontext or should I create separated ViewModel for AddEdit UserControl?

希望这听起来很清楚,如果不是请询问详情。

Hopefully this sounds clear, if not please ask for details.

推荐答案

第1部分显示在MVVM控件的属性

Part 1. Display the properties of the control in MVVM

正如我在评论说:

在MVVM视图模型不应该知道的控件,它的位置。在这种情况下,使用附加的行为或离开同方逻辑视图

In MVVM ViewModel should not know about the controls, which are located. In such cases, use the attached behavior or leave the same side logic in View

视图模型不直接与相关的查看,所以只是指控件的名称不会对。这将是最好的型号设置一个属性,并通过将其绑定到查看视图模型,但属性名称不支持绑定(引自 MSDN ):

ViewModel is not directly associated with a View, so just refer to the name of the control would not be right. It would be better to set a property in the Model, and bind it into View via ViewModel, but the property Name does not support Binding (quote from the MSDN):

数据绑定一个名字在技术上是可行的,但是是一个非常罕见的情况下,因为数据绑定名称不能成为财产的主要预期目标:提供code-后面的标识符的连接点。

Data binding a Name is technically possible, but is an extremely uncommon scenario because a data-bound Name cannot serve the main intended purpose of the property: to provide an identifier connection point for code-behind.

所以我建议使用标签属性或的Uid 。在我的例子(给出如下图),我用的Uid 属性为这些目的。

so I suggest to use the Tag property or Uid. In my example (give an below), I use Uid property for these purposes.

通过的ViewModels第2部分通信(模式中保)

Part 2. Communication via ViewModels (pattern Mediator)

有的中保的格局几个实施例,但我最喜欢的实施 XAML盖伊 这是简单而明确 - 的调解模式。

There are several embodiments of the Mediator pattern, but I like the most the implementation by XAML Guy, it is simple and clear - The Mediator Pattern.

实施code

public static class Mediator
{
    static IDictionary<string, List<Action<object>>> pl_dict = new Dictionary<string, List<Action<object>>>();

    static public void Register(string token, Action<object> callback)
    {
        if (!pl_dict.ContainsKey(token))
        {
            var list = new List<Action<object>>();
            list.Add(callback);
            pl_dict.Add(token, list);
        }
        else
        {
            bool found = false;
            foreach (var item in pl_dict[token])
                if (item.Method.ToString() == callback.Method.ToString())
                    found = true;
            if (!found)
                pl_dict[token].Add(callback);
        }
    }

    static public void Unregister(string token, Action<object> callback)
    {
        if (pl_dict.ContainsKey(token))
        {
            pl_dict[token].Remove(callback);
        }
    }

    static public void NotifyColleagues(string token, object args)
    {
        if (pl_dict.ContainsKey(token))
        {
            foreach (var callback in pl_dict[token])
                callback(args);
        }
    }
}

为了证明他的工作,我创建了一个小例子,它由两个浏览,每个人都有自己的视图模型型号

To demonstrate his work, I created a small example, which consists of two Views, each has its own ViewModel and Model.

该项目结构如下所示:

输出

当你点击按钮,ListOfData 视图模型通过中介与DataDetails 视图模型,从而通信

When you click on Button, ListOfData ViewModel communicates via mediator with DataDetails ViewModel, thus:

Mediator.NotifyColleagues("ShowDetails", true);
Mediator.NotifyColleagues("SetSelectedFruit", ListOfDataModel.FruitGreen);

这与性互动必须注册的所有程序的视图模型是这样的:

All procedures that interact with the properties must register their ViewModel like this:

private void ShowDetails_Mediator(object args)
{
    bool showDetails = (bool)args;

    if (showDetails == true)
    {
        DataDetailsModel.IsVisible = true;
    }
    else
    {
        DataDetailsModel.IsVisible = false;
    }
}

private void SetSelectedFruit_Mediator(object args)
{
    string selectedFruit = (string)args;

    DataDetailsModel.SelectedFruit = selectedFruit;
}

public DataDetailsViewModel() 
{
    DataDetailsModel = new DataDetailsModel();

    Mediator.Register("ShowDetails", ShowDetails_Mediator);
    Mediator.Register("SetSelectedFruit", SetSelectedFruit_Mediator);
}

在这个例子中我使用了的DataTemplate ,而不是用户控件。下面是该项目的主要部分:

In the example I used a DataTemplate instead UserControl. Below are the main part of the project:

MainWindow.xaml

<Window x:Class="CommunicateWithVM.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:ViewModels="clr-namespace:CommunicateWithVM.ViewModels"
    Title="MainWindow" 
    WindowStartupLocation="CenterScreen"
    Height="350"
    Width="525">

    <Grid>
        <ContentControl Name="ListOfData"
                        ContentTemplate="{StaticResource ListOfDataView}">

            <ViewModels:ListOfDataViewModel />            
        </ContentControl>

        <ContentControl Name="DataDetails"
                        ContentTemplate="{StaticResource DataDetailsView}">

            <ViewModels:DataDetailsViewModel />
        </ContentControl>
    </Grid>
</Window> 

模式

Models

DataDetailsModel

public class DataDetailsModel : NotificationObject
{
    #region SelectedFruit

    private string _selectedFruit = "";

    public string SelectedFruit
    {
        get
        {
            return _selectedFruit;
        }

        set
        {
            _selectedFruit = value;
            NotifyPropertyChanged("SelectedFruit");
        }
    }

    #endregion

    #region IsVisible

    private bool _isVisible = false;

    public bool IsVisible
    {
        get
        {
            return _isVisible;
        }

        set
        {
            _isVisible = value;
            NotifyPropertyChanged("IsVisible");
        }
    }

    #endregion
}

ListOfDataModel

public class ListOfDataModel : NotificationObject
{
    #region FruitGreen

    private string _fruitGreen = "Apple";

    public string FruitGreen
    {
        get
        {
            return _fruitGreen;
        }

        set
        {
            _fruitGreen = value;
            NotifyPropertyChanged("FruitGreen");
        }
    }

    #endregion

    #region FruitYellow

    private string _fruitYellow = "Limon";

    public string FruitYellow
    {
        get
        {
            return _fruitYellow;
        }

        set
        {
            _fruitYellow = value;
            NotifyPropertyChanged("FruitYellow");
        }
    }

    #endregion
}

的ViewModels

ViewModels

DataDetailsViewModel

public class DataDetailsViewModel
{
    #region DataDetailsModel

    private DataDetailsModel _dataDetailsModel = null;

    public DataDetailsModel DataDetailsModel
    {
        get
        {
            return _dataDetailsModel;
        }

        set
        {
            _dataDetailsModel = value;
        }
    }

    #endregion

    #region ShowDetails_Mediator

    private void ShowDetails_Mediator(object args)
    {
        bool showDetails = (bool)args;

        if (showDetails == true)
        {
            DataDetailsModel.IsVisible = true;
        }
        else
        {
            DataDetailsModel.IsVisible = false;
        }
    }

    #endregion

    #region SetSelectedFruit_Mediator

    private void SetSelectedFruit_Mediator(object args)
    {
        string selectedFruit = (string)args;

        DataDetailsModel.SelectedFruit = selectedFruit;
    }

    #endregion

    #region DataDetailsViewModel Constructor

    public DataDetailsViewModel() 
    {
        DataDetailsModel = new DataDetailsModel();

        Mediator.Register("ShowDetails", ShowDetails_Mediator);
        Mediator.Register("SetSelectedFruit", SetSelectedFruit_Mediator);
    }

    #endregion
}

ListOfDataViewModel

public class ListOfDataViewModel
{
    #region ListOfDataModel

    private ListOfDataModel _listOfDataModel = null;

    public ListOfDataModel ListOfDataModel
    {
        get
        {
            return _listOfDataModel;
        }

        set
        {
            _listOfDataModel = value;
        }
    }

    #endregion

    #region GreenButtonCommand

    private ICommand _greenButtonCommand = null;

    public ICommand GreenButtonCommand
    {
        get
        {
            if (_greenButtonCommand == null)
            {
                _greenButtonCommand = new RelayCommand(param => this.GreenButton(), null);
            }

            return _greenButtonCommand;
        }
    }

    private void GreenButton()
    {
        Mediator.NotifyColleagues("ShowDetails", true);
        Mediator.NotifyColleagues("SetSelectedFruit", ListOfDataModel.FruitGreen);
    }

    #endregion

    #region YellowButtonCommand

    private ICommand _yellowButtonCommand = null;

    public ICommand YellowButtonCommand
    {
        get
        {
            if (_yellowButtonCommand == null)
            {
                _yellowButtonCommand = new RelayCommand(param => this.YellowButton(), null);
            }

            return _yellowButtonCommand;
        }
    }

    private void YellowButton()
    {
        Mediator.NotifyColleagues("ShowDetails", true);
        Mediator.NotifyColleagues("SetSelectedFruit", ListOfDataModel.FruitYellow);
    }

    #endregion

    #region ListOfDataViewModel Constructor

    public ListOfDataViewModel() 
    {
        ListOfDataModel = new ListOfDataModel();
    }

    #endregion
}

浏览

Views

DataDetailsView

<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                xmlns:ViewModels="clr-namespace:CommunicateWithVM.ViewModels">

    <BooleanToVisibilityConverter x:Key="BooleanToVisibilityConverter" />

    <DataTemplate x:Key="DataDetailsView" DataType="{x:Type ViewModels:DataDetailsViewModel}">
        <StackPanel Width="200" 
                    Background="AliceBlue"
                    HorizontalAlignment="Right"
                    Visibility="{Binding Path=DataDetailsModel.IsVisible, 
                                         Converter={StaticResource BooleanToVisibilityConverter}}">

            <TextBlock Text="Fruit: " />
            <TextBlock Text="{Binding Path=DataDetailsModel.SelectedFruit}" />
        </StackPanel>
    </DataTemplate>    
</ResourceDictionary>

ListOfDataView

<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                    xmlns:ViewModels="clr-namespace:CommunicateWithVM.ViewModels">

    <DataTemplate x:Key="ListOfDataView" DataType="{x:Type ViewModels:ListOfDataViewModel}">
        <StackPanel Width="200" 
                    Background="Azure"
                    HorizontalAlignment="Left">

            <Button Uid="{Binding Path=ListOfDataModel.FruitGreen}"
                    Content="GreenButton"
                    Command="{Binding Path=GreenButtonCommand}" />

            <Button Uid="{Binding Path=ListOfDataModel.FruitYellow}"
                    Content="YellowButton" 
                    Command="{Binding Path=YellowButtonCommand}" />
        </StackPanel>
    </DataTemplate>    
</ResourceDictionary>

此项目可在此链接。