WPF中ReadOnlyObservableCollection设计时数据不显示的解决方法问询
解决ReadOnlyObservableCollection设计时数据不显示的问题
你的推测完全正确——Visual Studio的设计时数据系统无法直接填充ReadOnlyObservableCollection类型的只读属性,因为这个集合类型没有公开的添加元素接口,而且你的VM属性是get-only,XAML没法直接修改或初始化它。不过我们可以在不更改运行时集合只读性的前提下,通过两种优雅的方式解决这个问题:
方案1:添加设计时专用构造函数
这个方法无需修改现有属性,只需给ViewModel添加一个仅用于设计时的构造函数,让DesignData直接通过构造参数传递初始集合:
修改ViewModel代码
using System.Collections.Generic; using System.Collections.ObjectModel; using System.ComponentModel; using System.Windows; public class MyViewModel { private ObservableCollection<string> myProtectedCollection; public ReadOnlyObservableCollection<string> MyCollectionProperty { get; } // 运行时使用的默认构造函数 public MyViewModel() { myProtectedCollection = new ObservableCollection<string>(); MyCollectionProperty = new ReadOnlyObservableCollection<string>(myProtectedCollection); // 仅在运行时添加初始数据 if (!DesignerProperties.GetIsInDesignMode(new DependencyObject())) { myProtectedCollection.Add("String1"); myProtectedCollection.Add("String2"); myProtectedCollection.Add("String3"); } } // 设计时专用构造函数,接受初始集合数据 public MyViewModel(IEnumerable<string> initialDesignData) { myProtectedCollection = new ObservableCollection<string>(initialDesignData); MyCollectionProperty = new ReadOnlyObservableCollection<string>(myProtectedCollection); } }
更新DesignData文件(TestData.xaml)
使用ObjectDataProvider来调用新的构造函数,传递设计时数据:
<ObjectDataProvider xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="clr-namespace:ScrapWpfApplication1" xmlns:system="clr-namespace:System;assembly=mscorlib" ObjectType="{x:Type local:MyViewModel}"> <ObjectDataProvider.ConstructorParameters> <x:Array Type="{x:Type system:String}"> <system:String>String 1</system:String> <system:String>String 2</system:String> </x:Array> </ObjectDataProvider.ConstructorParameters> </ObjectDataProvider>
方案2:添加设计时专用可写属性
如果你不想添加构造函数,可以给ViewModel添加一个仅在设计模式下生效的可写属性,用来间接填充内部集合:
修改ViewModel代码
using System.Collections.ObjectModel; using System.ComponentModel; using System.Windows; using System.Windows.Markup; public class MyViewModel { private ObservableCollection<string> myProtectedCollection; public ReadOnlyObservableCollection<string> MyCollectionProperty { get; } // 仅设计时可见的可写属性,用于填充集合 [DesignOnly(true)] public ObservableCollection<string> DesignTimeCollection { set { // 只在设计模式下执行逻辑 if (DesignerProperties.GetIsInDesignMode(new DependencyObject())) { myProtectedCollection.Clear(); foreach (var item in value) { myProtectedCollection.Add(item); } } } } public MyViewModel() { myProtectedCollection = new ObservableCollection<string>(); MyCollectionProperty = new ReadOnlyObservableCollection<string>(myProtectedCollection); // 运行时初始数据 if (!DesignerProperties.GetIsInDesignMode(new DependencyObject())) { myProtectedCollection.Add("String1"); myProtectedCollection.Add("String2"); myProtectedCollection.Add("String3"); } } }
更新DesignData文件(TestData.xaml)
设置这个设计时专用属性:
<local:MyViewModel xmlns:local="clr-namespace:ScrapWpfApplication1" xmlns:system="clr-namespace:System;assembly=mscorlib"> <local:MyViewModel.DesignTimeCollection> <system:String>String 1</system:String> <system:String>String 2</system:String> </local:MyViewModel.DesignTimeCollection> </local:MyViewModel>
最后注意事项
别忘了修正MainWindow.xaml里的设计数据路径——你创建的是TestData.xaml,但当前代码里引用的是SampleData.xaml,需要改成:
d:DataContext="{d:DesignData Source=TestData.xaml}"
两种方案都能保证运行时MyCollectionProperty的只读性,同时让设计器正确显示示例数据。个人更推荐方案1,因为它的逻辑更简洁,不需要额外的属性,完全通过构造函数初始化数据,符合单一职责原则。
内容的提问来源于stack exchange,提问作者Martin Brown
相关产品推荐
相关产品推荐

