【问题标题】:ObservableCollection Property ClassObservableCollection 属性类
【发布时间】:2013-01-10 15:27:39
【问题描述】:

我在我的MVVM 项目中重复这个属性太多次了,数不过来。创建通用类或工厂来替换这些代码行的正确方法是什么?

    ObservableCollection<Result> _resultCollection;
    public ObservableCollection<Result> ResultCollection
    {
        get
        {
            if (_resultCollection == null)
                _resultCollection = new ObservableCollection<Result>();
            return _resultCollection;
        }
        set
        {
            _resultCollection = value;
        }
    }

【问题讨论】:

  • 不可以放在基类中

标签: c# asp.net generics mvvm


【解决方案1】:

我知道这并不能完全回答您的问题,但我个人更喜欢在 Visual Studio 中使用录制的宏来为我编写所有这些内容。

我更喜欢使用它而不是泛型类,因为它将所有相关代码保存在一个地方,并且很容易理解发生了什么。通常,我将所有私有字段放在班级的顶部,并将所有公共属性隐藏在 #region 标记中,我一直处于折叠状态,因此无需滚动浏览它们。

在 VS 中创建宏相当容易:只需转到工具 > 宏 > 录制临时宏,然后仅使用键盘执行您想要的更改。让宏正常工作后,只需将其保存为永久宏即可。如果你做得对,你可以用任何变量重新运行宏,它会以同样的方式构建它。

创建宏时要记住的一些有用的键盘快捷键是:

  • Ctrl+C/Ctrl+V复制/粘贴
  • Ctrl+Right/Ctrl+Left 移动单词
  • Home/End 移动到行首或行尾
  • Shift 用于在移动光标时突出显示单词

您可能还需要对 VB 宏代码进行一些小的修改,例如 .Replace() 将小写字母变为大写字母。

这是一个示例宏,我使用它来构建我所有的 MVVM 公共属性。它使用 PRISM 库,因此它使用语法RaisePropertyChanged(() =&gt; this.SomeProperty);

Sub PRISM_BuildPropertyChanged_CursorAtDefaultAfterCtrlRE()
    DTE.ActiveDocument.Selection.LineDown(False, 2)
    DTE.ActiveDocument.Selection.WordLeft(True)
    DTE.ActiveDocument.Selection.Copy()
    DTE.ActiveDocument.Selection.LineDown(False, 3)
    DTE.ActiveDocument.Selection.EndOfLine()
    DTE.ActiveDocument.Selection.CharLeft()
    DTE.ActiveDocument.Selection.NewLine()
    DTE.ActiveDocument.Selection.Text = "RaisePropertyChanged(() => this."
    DTE.ActiveDocument.Selection.Paste()
    DTE.ActiveDocument.Selection.Text = ");"
    DTE.ActiveDocument.Selection.LineUp()
    DTE.ActiveDocument.Selection.StartOfLine(vsStartOfLineOptions.vsStartOfLineOptionsFirstText)
    DTE.ActiveDocument.Selection.CharRight(False, 4)
    DTE.ActiveDocument.Selection.NewLine()
    DTE.ActiveDocument.Selection.CharRight()
    DTE.ActiveDocument.Selection.NewLine()
    DTE.ActiveDocument.Selection.Text = "if (value != "
    DTE.ActiveDocument.Selection.WordRight(True)
    DTE.ActiveDocument.Selection.Copy()
    DTE.ActiveDocument.Selection.CharLeft()
    DTE.ActiveDocument.Selection.Paste()
    DTE.ActiveDocument.Selection.DeleteLeft()
    DTE.ActiveDocument.Selection.Text = ")"
    DTE.ActiveDocument.Selection.NewLine()
    DTE.ActiveDocument.Selection.Text = "{"
    DTE.ActiveDocument.Selection.LineDown()
    DTE.ActiveDocument.Selection.EndOfLine()
    DTE.ActiveDocument.Selection.NewLine()
    DTE.ActiveDocument.Selection.Text = "}"
End Sub

有了它,我就可以写出我的私有定义了,比如

private ObservableCollection<SomeObject> _someCollection;

Ctrl+RE构建公共属性定义

private ObservableCollection<SomeObject> _someCollection;

public ObservableCollection<SomeObject> SomeCollection
{
    get { return _someCollection; }
    set { _someCollection = value; }
}

然后运行我的宏来构建属性更改通知

public ObservableCollection<SomeObject> SomeCollection
{
    get { return _someCollection; }
    set 
    {
        if (value != _someCollection)
        {
            _someCollection = value;
            RaisePropertyChanged(() => this.SomeCollection);
        }
    }
}

(默认情况下,Ctrl+R, E 将光标放在私有字段定义的末尾,所以这是运行时光标需要的位置宏)

我还有另一个旧宏,它添加了检查值是否为空的代码,如果是,则将其设置为对象的新实例,但我从未使用过太多,因为我更喜欢在构造函数而不是在属性定义中。

如果需要,它看起来像这样(注意宏名称 - 您需要在运行此宏之前突出显示公共属性名称):

Sub CreatePublicGet_NoNull_HightlightPropertyNameFirst()
    DTE.ActiveDocument.Selection.CharLeft(False, 2)
    DTE.ActiveDocument.Selection.StartOfLine(vsStartOfLineOptions.vsStartOfLineOptionsFirstText, True)
    DTE.ActiveDocument.Selection.WordRight(True)
    DTE.ActiveDocument.Selection.Copy()
    DTE.ActiveDocument.Selection.LineDown(False, 2)
    DTE.ActiveDocument.Selection.StartOfLine(vsStartOfLineOptions.vsStartOfLineOptionsFirstText)
    DTE.ActiveDocument.Selection.CharRight(False, 4)
    DTE.ActiveDocument.Selection.NewLine()
    DTE.ActiveDocument.Selection.CharRight()
    DTE.ActiveDocument.Selection.NewLine()
    DTE.ActiveDocument.Selection.EndOfLine()
    DTE.ActiveDocument.Selection.CharLeft()
    DTE.ActiveDocument.Selection.NewLine()
    DTE.ActiveDocument.Selection.LineUp(False, 2)
    DTE.ActiveDocument.Selection.EndOfLine()
    DTE.ActiveDocument.Selection.NewLine()
    DTE.ActiveDocument.Selection.Paste()
    DTE.ActiveDocument.Selection.Text = "();"
    DTE.ActiveDocument.Selection.LineDown()
    DTE.ActiveDocument.Selection.WordLeft(True)
    DTE.ActiveDocument.Selection.CharLeft()
    DTE.ActiveDocument.Selection.WordLeft(True)
    DTE.ActiveDocument.Selection.Copy()
    DTE.ActiveDocument.Selection.LineUp(False, 2)
    DTE.ActiveDocument.Selection.NewLine()
    DTE.ActiveDocument.Selection.Text = "if ("
    DTE.ActiveDocument.Selection.Paste()
    DTE.ActiveDocument.Selection.Text = " == null)"
    DTE.ActiveDocument.Selection.NewLine()
    DTE.ActiveDocument.Selection.Text = "{"
    DTE.ActiveDocument.Selection.LineDown()
    DTE.ActiveDocument.Selection.StartOfLine(vsStartOfLineOptions.vsStartOfLineOptionsFirstText)
    DTE.ActiveDocument.Selection.EndOfLine()
    DTE.ActiveDocument.Selection.NewLine()
    DTE.ActiveDocument.Selection.Text = "}"
    DTE.ActiveDocument.Selection.LineUp()
    DTE.ActiveDocument.Selection.StartOfLine(vsStartOfLineOptions.vsStartOfLineOptionsFirstText)
    DTE.ActiveDocument.Selection.Paste()
    DTE.ActiveDocument.Selection.Text = " = new "
    DTE.ActiveDocument.Selection.Collapse()
    DTE.ActiveDocument.Selection.EndOfLine()
    DTE.ActiveDocument.Selection.LineDown(False, 4)
    DTE.ActiveDocument.Selection.EndOfLine()
    DTE.ActiveDocument.Selection.LineUp(True)
    DTE.ActiveDocument.Selection.Delete()
End Sub

结果代码如下:

public ObservableCollection<SomeObject> SomeCollection
{
    get 
    {
        if (_someCollection == null)
        {
            _someCollection = new ObservableCollection<SomeObject>();
        }
        return _someCollection; 
    }
}

【讨论】:

  • 这令人印象深刻。
  • @贾斯汀谢谢!我已经够懒了,以后我会更加努力地想办法变懒;)
【解决方案2】:

AFAIK 没有某种形式的代码生成是不可能的。查看 T4 模板,如回答 here .

【讨论】:

    【解决方案3】:
    public abstract class XBase<T>
    {
        ObservableCollection<T> _resultCollection;
    
        public ObservableCollection<T> ResultCollection
        {
            get
            {
                if (_resultCollection == null)
                    _resultCollection = new ObservableCollection<T>();
    
                return _resultCollection;
            }
            set
            {
                _resultCollection = value;
    
            }
        }
    }
    

    【讨论】:

      【解决方案4】:

      是的...能够绑定到一个字段会很棒,不是吗... ;-) 这让我想到了第一个解决方案

      (1) 创建一个允许您绑定字段的转换器。

      public class FieldBindingConverter : IValueConverter
      {
          public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
          {
              var par = parameter as string;
              var field = value.GetType().GetField(par);
              return field.GetValue(value);
          }
      
          public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
          {
              throw new NotImplementedException();
          }
      }
      

      然后 (2) 直接从 XAML 绑定字段:

          <ItemsControl ItemsSource="{Binding ., Converter={StaticResource Field}, ConverterParameter=Coll2}" Grid.Row="1">
      

      说实话,我有点不喜欢这样,因为它并没有真正弄清楚事情......所以作为第二种解决方案,您可以直接从构造函数实例化属性:

      public Foo() 
      {
          ResultCollection = new ObservableCollection<Result>();
      }
      
      [...]
      
      public ObservableCollection<Result> ResultCollection { get; private set; }
      

      虽然这确实会增加您的内存占用几个字节(它将延迟实例化的语义更改为直接实例化),但我不会太在意这一点。

      【讨论】:

        猜你喜欢
        • 2021-11-08
        • 1970-01-01
        • 2019-10-31
        • 2016-07-10
        • 1970-01-01
        • 1970-01-01
        • 2014-10-26
        • 2011-12-31
        • 2011-02-14
        相关资源
        最近更新 更多