绑定到包含MVVM中集合的模型

时间:2012-12-07 18:44:47

标签: c# mvvm collections caliburn.micro

我开始使用MVVM(使用Caliburn.Micro)并遇到了一个我不确定我是否正确执行此操作的问题。我有一个模型MediaCacherConfig,它表示以json格式存储数据的文本文件。该模型包含2个字符串列表和一个字符串。

我正在努力的是如何正确设置viewmodel,特别是AddNewFolder()方法。我不确定我是否正在提出正确的事件以及viewmodel的表示是否正确。我可以看到如何绑定到一个简单的属性,但绑定到一个集合似乎更像是一个头部微调器,因为我每次添加一个项目(字符串)时都会创建一个全新的集合。

此外,当我加载一个全新的模型时,我必须对所有对我没有意义的属性运行NotifyPropertyChanged()方法。

非常感谢任何指导。

public class MediaCacherConfig : IConfig
{

    public string DatabaseFileName { get; set; }

    public ICollection<string> FoldersToScan { get; set; }

    public ICollection<string> ExtensionsToIgnore { get; set; }

}

我有一个viewmodel MediaCacherConfigViewModel

    public class MediaCacherConfigViewModel : PropertyChangedBase
{

    private MediaCacherConfig Model { get; set; }

    public string DatabaseFileName
    {
        get { return Model.DatabaseFileName; }
        set
        {
            Model.DatabaseFileName = value;
            NotifyOfPropertyChange(() => DatabaseFileName);
        }
    }

    public BindableCollection<string> FoldersToScan
    {
        get
        {
            return new BindableCollection<string>(Model.FoldersToScan);
        }
        set
        {
            Model.FoldersToScan = value;
            NotifyOfPropertyChange(() => FoldersToScan);
        }
    }

    public BindableCollection<string> ExtensionsToIgnore
    {
        get
        {
            return new BindableCollection<string>(Model.ExtensionsToIgnore);
        }
        set
        {
            Model.ExtensionsToIgnore = value;
            NotifyOfPropertyChange(() => ExtensionsToIgnore);
        }
    }

    /* Constructor */
    public MediaCacherConfigViewModel()
    {
        LoadSampleConfig();
    }

    /* Methods */
    public void LoadSampleConfig()
    {

        MediaCacherConfig c = new MediaCacherConfig();

        string sampleDatabaseFileName = "testing.config";

        List<string> sampleFoldersToScan = new List<string>();
        sampleFoldersToScan.Add("A");
        sampleFoldersToScan.Add("B");
        sampleFoldersToScan.Add("C");

        List<string> sampleExtensionsToIgnore = new List<string>();
        sampleExtensionsToIgnore.Add("txt");
        sampleExtensionsToIgnore.Add("mov");
        sampleExtensionsToIgnore.Add("db");
        sampleExtensionsToIgnore.Add("dat");

        c.DatabaseFileName = sampleDatabaseFileName;
        c.FoldersToScan = sampleFoldersToScan;
        c.ExtensionsToIgnore = sampleExtensionsToIgnore;

        Model = c;

        NotifyOfPropertyChange(() => DatabaseFileName);
        NotifyOfPropertyChange(() => FoldersToScan);
        NotifyOfPropertyChange(() => ExtensionsToIgnore);


    }

    public void AddNewFolder()
    {
        Model.FoldersToScan.Add("new one added");
        NotifyOfPropertyChange(() => FoldersToScan);

    }

    public void SaveConfig()
    {
        ConfigTools.Configure(Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData), "Cacher", "Config"));

        ConfigTools.SaveConfig(Model,"sampleconfig.txt");              
    }

    public void LoadConfig()
    {
        ConfigTools.Configure(Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData), "Cacher", "Config"));

        MediaCacherConfig m = ConfigTools.LoadConfig<MediaCacherConfig>("sampleconfig.txt") as MediaCacherConfig;
        Model = m;


        NotifyOfPropertyChange(() => DatabaseFileName);
        NotifyOfPropertyChange(() => FoldersToScan);
        NotifyOfPropertyChange(() => ExtensionsToIgnore);

    }
}

以下是我的观点:

<UserControl x:Class="MediaCacher.Views.MediaCacherConfigView"
         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" 
         mc:Ignorable="d" 
         d:DesignHeight="413" Width="300">
<Grid MinWidth="300" MinHeight="300" Background="LightBlue" Margin="0,0,0,0">
    <Grid.RowDefinitions>
        <RowDefinition Height="409*"/>
        <RowDefinition Height="4*"/>
    </Grid.RowDefinitions>
    <TextBox x:Name="DatabaseFileName" TextWrapping="Wrap" Margin="10,64,10,0" HorizontalAlignment="Center" Width="280" Height="42" VerticalAlignment="Top"/>
    <ListBox x:Name="FoldersToScan" HorizontalAlignment="Left" Height="145" Margin="10,111,0,0" VerticalAlignment="Top" Width="280"/>
    <ListBox x:Name="ExtensionsToIgnore" HorizontalAlignment="Left" Height="145" Margin="10,261,0,0" VerticalAlignment="Top" Width="280"/>
    <Button x:Name="AddNewFolder" Content="Add" HorizontalAlignment="Left" Margin="10,10,0,0" VerticalAlignment="Top" Width="87" Height="49"/>
    <Button x:Name="LoadConfig" Content="Load" HorizontalAlignment="Left" Margin="102,10,0,0" VerticalAlignment="Top" Width="96" Height="49"/>
    <Button x:Name="SaveConfig" Content="Save" HorizontalAlignment="Left" Margin="203,10,0,0" VerticalAlignment="Top" Width="87" Height="49"/>
</Grid>

1 个答案:

答案 0 :(得分:2)

首先,您每次都会返回一个全新的系列,所以很明显没有任何东西可以保留。

 public BindableCollection<string> FoldersToScan
    {
        get
        {
            return new BindableCollection<string>(Model.FoldersToScan);
        }
        set
        {
            Model.FoldersToScan = value;
            NotifyOfPropertyChange(() => FoldersToScan);
        }
    }

其次,您的AddFolder方法应该属于您的ViewModel。当您将字符串添加到现有集合时,它是BindingCollection的事实应该自动向View发起一个事件,即添加了一个新项目。


我就是这样做的。这显然是用于演示目的的示例,请添加您需要的所有其他内容。你想要传递EventArgs,并注意我没有实现INotifyPorpertyChanged,因为我没有时间全力以赴。我也在使用ObservableCollection,但您可以使用BindableCollection

此示例的目的是向您展示如何管理ViewModel - &gt;模型沟通。从技术上讲,你的观点 - &gt; ViewModel应该通过CommandPattern进行交谈。

public class YourViewModel
    {
        private readonly YourModel model;
        private ObservableCollection<string> foldersToScan = new ObservableCollection<string>();
        public ObservableCollection<string> FoldersToScan
        {
            get { return this.foldersToScan; }
        }

        public YourViewModel(YourModel model)
        {
            this.model = model;
            this.model.OnItemAdded += item => this.foldersToScan.Add(item);
        }

        public void AddFolder(string addFolder) //gets called from view
        {
            this.model.AddFolder(addFolder); //could be ICommand using Command Pattern
        }
    }

    public class YourModel
    {
        private readonly List<string> foldersToScan;
        public IEnumerable<string>  FoldersToScan
        {
            get { return this.foldersToScan; }
        }


        public event Action<string> OnItemAdded; 

        public YourModel()
        {
            this.foldersToScan = new List<string>();
        }

        public void AddFolder(string folder)
        {
            this.foldersToScan.Add(folder);
            this.RaiseItemAdded(folder);
        }

        void RaiseItemAdded(string folder)
        {
            Action<string> handler = OnItemAdded;
            if (handler != null) handler(folder);
        }
    }