|
我有一个想法,是跟据Prism框架想到的
在Prism框架,我们经常会用到
<Grid DockPanel.Dock="Left"> <ContentControl prism:RegionManager.RegionName="{x:Static region:RegionNames.HeaderRegion}" /> public class RegionNames { public static readonly string HeaderRegion = nameof(HeaderRegion); public static readonly string AsideRegion = nameof(AsideRegion); public static readonly string ContentRegion = nameof(ContentRegion); public static readonly string FooterRegion = nameof(FooterRegion); public static readonly string SettingsRegion = nameof(SettingsRegion); public static readonly string SettingsTabRegion = nameof(SettingsTabRegion); }因为Prism有 RegionManager.RegisterViewWithRegion(RegionNames.AsideRegion, typeof(AsideView));可以实现将数据填充到界面,并且他这种区域管理非常优秀,我在不使用Prism框架的时候就必须写成了
<DataTemplate DataType="{x:Type viewModel:UniformGridViewModel}"> <view:UniformGridView /></DataTemplate> <ContentControl Grid.Row="0" Content="{StaticResource UniformGridView}" />不管是直接在ContentControl绑定View还是绑定ViewModel都没有Prism这种方法简便,我突发奇想,我在WPF原生的ContentControl使用静态绑定,由于Content可以绑定object,所以我新写的
<ContentControl Grid.Row="4" Content="{x:Static region:RegionNames.Header}" /> public class RegionNames { public const string Header = nameof(Header); }是完全可以绑定成功的,然后我发现了网上有人对Grid进行改造,它使用了
[TypeConverter(typeof(GridLengthCollectionConverter))] public class GridLengthCollection : ReadOnlyCollection<GridLength> { public GridLengthCollection(IList<GridLength> list) : base(list) { } }和 public class GridLengthCollectionConverter : TypeConverter { public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType) { if (sourceType == typeof(string)) return true; return base.CanConvertFrom(context, sourceType); } public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType) { if (destinationType == typeof(string)) return true; return base.CanConvertTo(context, destinationType); } public override object ConvertFrom(ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value) { string s = value as string; if (s != null) return ParseString(s, culture); return base.ConvertFrom(context, culture, value); } public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType) { if (destinationType == typeof(string) && value is GridLengthCollection) return ToString((GridLengthCollection)value, culture); return base.ConvertTo(context, culture, value, destinationType); } private string ToString(GridLengthCollection value, CultureInfo culture) { var converter = new GridLengthConverter(); return string.Join(",", value.Select(v => converter.ConvertToString(v))); } private GridLengthCollection ParseString(string s, CultureInfo culture) { var converter = new GridLengthConverter(); var lengths = s.Split(',').Select(p => (GridLength)converter.ConvertFromString(p.Trim())); return new GridLengthCollection(lengths.ToArray()); } }我准备试试TypeConverter转换的强大,那么我上面提到的我结合Prism的想法,并且我对原生WPF的ContentControl的Content绑定一个静态的属性,使用TypeConverter能不能做到类似Prism框架那样显示的不是字符串而是一个界面,我开始进行封装
public class RegionConverter : TypeConverter{ public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType) { return sourceType == typeof(string); } public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value) { if (value is string regionName) { switch (regionName) { case RegionNames.Header: return new HeaderView(); case RegionNames.Aside: return new AsideView(); case RegionNames.Content: return new ContentView(); default: throw new ArgumentException($"Unknown region: {regionName}"); } } throw new NotSupportedException(); }}<Window.Resources> <!-- 注册 TypeConverter --> <local:RegionConverter x:Key="RegionConverter" /> </Window.Resources> <Grid> <!-- 绑定区域名称并使用 TypeConverter 转换为 View --> <ContentControl Content="{Binding Source={x:Static local:RegionNames.Header}, Converter={StaticResource RegionConverter}}" /> </Grid>但是编译不通过,并且运行失败,
我强制运行后程序报错InvalidCastException: Unable to cast object of type '自定义一个区域管理.RegionConverter' to type 'System.Windows.Data.IValueConverter'.在思考转换的时候,我想到WPF的IValueConverter也可以转换,我继续尝试
public class RegionConverter : IValueConverter{ public object Convert(object value, Type targetType, object parameter, CultureInfo culture) { if (value is string regionName) { switch (regionName) { case RegionNames.Header: return new HeaderView(); case RegionNames.Aside: return new AsideView(); case RegionNames.Content: return new ContentView(); default: throw new ArgumentException($"Unknown region: {regionName}"); } } throw new NotSupportedException(); } public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) { throw new NotSupportedException(); }}完美的解决了这个问题,以后不需要直接绑定View或者ViewModel,通过字符串进行转换,只需要在XAML绑定字符串即可
<ContentControl Content="{Binding Source={x:Static region:RegionNames.Header}, Converter={StaticResource RegionConverter}}" /> |
|