如何为用户控件创建MVVM而不将所有内部暴露给消费者?

时间:2012-04-10 08:48:54

标签: wpf mvvm binding user-controls

我正在使用MVVM设计一些复杂的UserControl。 UserControls的datacontext处理所有逻辑,并将UserControl交互的结果提供给控件的使用者。

我面临的问题是看起来像内部不适用于绑定,如果我尝试绑定到非公共绑定的源属性不起作用。如果我必须打开(amek public)我的VM顶部的所有源属性能够绑定它们,那么我打开UserControl和消费者的内部:

  1. 对使用什么属性感到困惑。
  2. 可以使用非外部使用的属性来破坏事物。
  3. 有什么想法吗?

    提前致谢。

    编辑:嗯,看this看来,来源必须始终公开,对。但后来我仍然面临着公开内幕的问题。任何解决方案?

    示例:

    您创建一个通用的UserControl。控件应该是一个黑盒子,它接受最终用户提供的查询,以某种方式向他显示查询返回的行,并让UserControl使用者获取所选值。该控件使用显示行的Combobox实现。

    UserControl在UserControl.xaml中,并且有一个UserControlVM.cs文件,其中包含UserControl的ViewModel。

    ViewModel包含要执行的查询,查询返回的项目列表以及所选项目。项目列表和所选项目是公共的,可以绑定到UserControl。

    使用者在其窗口内使用UserControl的一个实例,在其窗口视图模型中使用UserControlVM的一个实例。我面临的问题是,当用户只能访问所选项目时,最终用户可以访问查询返回的项目列表。

3 个答案:

答案 0 :(得分:0)

Binding类的所有属性定位都需要公开

这是一种可能的替代解决方案:

您的DataContext(如果是ViewModel)可能会实施INotifyPropertyChanged。 如果是这样,那么当您在DataContext上设置ViewModelUserControl)时,只需订阅PropertyChanged事件处理程序,然后在代码隐藏中编写逻辑来操作您的控制取决于更改的数据。

答案 1 :(得分:0)

作为类的实现细节的数据不应该是公开的,但在我看来,您甚至不应该想要绑定到这种类型的数据。绑定到视图的数据应该是视图模型的公共属性和命令。请注意,视图模型定义了您的UI

用户界面不允许更改的公共属性应该只实现getter而不是setter。只能在特定条件下更改的公共属性应在setter中强制执行这些条件。视图模型应该提供并容纳所有UI逻辑(属性和命令)。

您还应该将视图模型包装在测试所有这些内容的单元测试中。

根据评论反馈进行更新:

class MyViewModel : ViewModelBase
{
     private bool _showSomething;
     public bool ShowSomething
     {
         get { return _showSomething; }
         set
         {
             _showSomething = value;
             RaisePropertyChanged("ShowSomething");
             RaisePropertyChanged("TheThing");
         }
     }

     public Something TheThing
     { 
         get
         {
             if(_showSomething) { return _theThing; }
             return _theOtherThing;
         }
     }

     private Something _theThing;
     private Something _theOtherThing;

}

编辑*:以下内容可能更接近基于评论的内容。

public interface IQueryControl
{
    string Query { get; set; } //view passes query in
    ReadOnlyCollection<string> QueryResultDescriptions { get; } //bind to combo items
    string SelectedQueryDescription { get; set; } //bind to combo selection
    object SelectedItem { get; } //resulting object
}

public class UserControlVM : ViewModelBase, IQueryControl
{
    private string _query;
    private ObservableCollection<object> _queryResults;
    private ReadOnlyCollection<string> _externalResults;
    private object _selectedResult;

    public string Query 
    { 
        get { return _query; } 
        set 
        {
            _query = value; 
            RaisePropertyChanged("Query");
            UpdateQueryResults(); 
        }
    }

    private void UpdateQueryResults()
    {
        //Do query which allocates / updates _queryResults;
        _externalResults = new ReadOnlyCollection<string>((from entry in _queryResults select entry.ToString()).ToList<string>());
        RaisePropertyChanged("QueryResultDescriptions");
    }

    public ReadOnlyCollection<string> QueryResultDescriptions
    {
        get { return _externalResults; }
    }

    public string SelectedQueryDescription
    {
        get { return _selectedResult.ToString(); }
        set
        {
            SelectResult(value);
        }
    }

    private void SelectResult(string value)
    {
        Dictionary<string, object> lookup = _queryResults.ToDictionary<object, string>((result) => { return result.ToString(); });
        if (lookup.ContainsKey(value))
        {
            _selectedResult = lookup[value];
            RaisePropertyChanged("SelectedQueryDescription");
            RaisePropertyChanged("SelectedItem");
        }
        else
        {
            //throw something
        }
    }

    public object SelectedItem
    {
        get { return _selectedResult; }
    }

}

答案 2 :(得分:0)

我找到了一个解决方案(但是如果您的ViewModel需要构造函数参数,我会推进您的工作无效。)

  1. 在保持绑定属性公开的同时使ViewModel成为内部。
  2. 创建一个“UserControlData”类,其中包含usercontrol将为最终用户提供的数据
  3. 通过UserControl依赖属性提供该类的实例。
  4. usercontrol使用者必须在其viewmodel中绑定UserControlData实例以获取usercontrol结果。