场景:作为一名嵌入式软件工程师,我希望我的MVVM视图能够在我的XAML样式中设置多个属性,从单个绑定到我的ViewModel。
鉴于:我的视图包含一个应用于Button的样式,而我的ViewModel有一个'键'属性。
ButtonStyle.xaml ResourceDictionary的相关位:
<!-- This is the style used for Buttons -->
<Style x:Key="ButtonStyle" TargetType="{x:Type Button}">
<!-- Other, common Style setters live here -->
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Button}">
<Border x:Name="ButtonBorder">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="auto"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Image Grid.Column="0"
Source="{Binding Path=Key,
Converter={r:ButtonImageConverter}}"/>
<Label Grid.Column="1"
Content="{Binding Path=Key,
Converter={r:ButtonTextConverter}}"/>
</Grid>
</Border>
<!-- ControlTemplate.Triggers and other stuff -->
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
CommandViewModel.cs类的相关位:
// The relevant bits of the ViewModel class that the button is bound to
public class CommandViewModel: INotifyPropertyChanged, ICommand
{
private String Key;
{
get { return key; }
set { OnPropertyChanged(ref key, value); }
}
// ... Plus implementation of OnPropertyChanged, ICommand, etc.
}
时间:按钮绑定到ViewModel对象。
从ViewModel到Button的绑定:
<Button Style="{DynamicResource ButtonStyle}"
DataContext="{Binding Path=CloseCommand}"
Content="{Binding Path=Key}"
Command="{Binding Path=Command}"
CommandParameter="{Binding}"/>
然后:根据与&#39;键相关联的资源集合设置了几个样式属性(使用值转换器)。属性。
其中一个价值转换器的执行不完整(另一个非常相似,此处未包含):
public class ButtonTextConverter : MarkupExtension, IValueConverter
{
// This is just so that I can reference it in the XAML as:
// Converter={r:ButtonImageConverter}
public override object ProvideValue(IServiceProvider serviceProvider)
{
return this;
}
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
String index = value as String;
if (String.IsNullOrEmpty(index))
{
throw new ArgumentException("The name to convert cannot be Null or Empty", "value");
}
// The code gets here just fine, with index containing the same
// string as the CommandViewModel.Key property.
// But ...
// How can I find a the String Resouce, using the index, when
// there may be several all related to the same Control? I.e.
// the ButtonImageConverter will also find a String containing
// the URI path for the image to display on the Button.
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
return String.Empty;
}
}
在架构上,我的View是一个程序集,它包含我的所有XAML(任何XAML组件都没有代码)和资源。 ViewModel位于不同的程序集中,对资源,图像,本地化文本,文化等一无所知。
一个会破坏我的架构的解决方案是向ViewModel类添加更多属性,并让它从XML文件中提取各种值。我不喜欢这样,因为我的模型和ViewModel都受到View内容的污染。我想在视图中保留所有这些内容。
我使用自定义资源类查看了另一个解决方案。 ResourceManager.GetObject Method页面包含一些示例代码,用于创建包含自定义资源的.RESX
资源文件。我已经尝试过了,但结果超出了我目前的理解范围,而且我的客户最大利益是尝试使用Microsoft ResX架构。但是,这可能是要走的路,如果有一种简单的方法可以将复杂对象(值转换器可以用来将Key
解复用)保存为单个资源实体。
我有哪些选项可以允许不同的资源通过相同的标识&#39;唯一标识,具体取决于哪个值转换器尝试查找资源?
答案 0 :(得分:0)
我实现的解决方案有点粗糙,并且依赖于为资源提供不同的名称,这些名称是通过将属性名称附加到资源“Key”名称而形成的。我已将其封装到ButtonResources
类中,ValueConverter
类用作帮助程序来获取实际资源。
internal class ButtonResources
{
#region Static Fields
private static ResourceManager resourceManager =
new ResourceManager("MyApplication.Resources.ButtonResources",
typeof(ButtonResources).Assembly);
private static Dictionary<String,ButtonResources> resourceDictionary =
new Dictionary<String, ButtonResources>();
#endregion
#region Instance Properties
internal String Text { get; set; }
internal BitmapImage Image { get; set; }
internal SolidColorBrush Brush { get; set; }
#endregion
#region Instance Fields
private String Name { get; set; }
#endregion
private ButtonResources(String buttonName)
{
this.Name = buttonName;
}
private void LoadResources(CultureInfo culture)
{
// Load the button Text
Text = resourceManager.GetString(Name + "Text", culture);
// Load the image file from a URI.
// See https://msdn.microsoft.com/en-us/library/aa970069(v=vs.100).aspx
// (It's regrettably complex, but there are lots of examples on t'interweb.)
String imageName = resourceManager.GetString(Name + "Image", culture);
Image = new BitmapImage(new Uri(@"pack://application:,,,/"
+ Assembly.GetExecutingAssembly().GetName().Name
+ ";component/Resources/"
+ imageName, UriKind.Absolute));
// Convert the colour resource to a suitable Brush object.
String colourName = resourceManager.GetString(Name + "Colour", culture);
Brush = new BrushConverter().ConvertFromString(colourName) as SolidColorBrush;
}
internal static ButtonResources FindButton(String buttonName, CultureInfo culture)
{
if (String.IsNullOrEmpty(buttonName))
{
throw new ArgumentException("The name of the button cannot be Null or Empty",
"buttonName");
}
ButtonResources buttonResources;
// Check whether the requested resource has been loaded yet.
if (resourceDictionary.ContainsKey(buttonName))
{
buttonResources = resourceDictionary[buttonName];
}
else
{
// Create a new instance with the requested name.
buttonResources = new ButtonResources(buttonName);
// Load the object's properties from the Resources.
buttonResources.LoadResources(culture);
// Add the new object to the dictionary.
resourceDictionary.Add(buttonName, buttonResources);
}
return buttonResources;
}
}
每个ValueConverter
类都有所简化:
public abstract class ValueConverter
: MarkupExtension
{
public override object ProvideValue(IServiceProvider serviceProvider)
{
return this;
}
public virtual object ConvertBack(object value, Type targetType,
object parameter, CultureInfo culture)
{
return String.Empty;
}
}
public class ButtonTextConverter : ValueConverter, IValueConverter
{
public object Convert(object value, Type targetType,
object parameter, CultureInfo culture)
{
// Use the ButtonResources helper to find the text to place on the button.
return ButtonResources.FindButton(value as String, culture).Text;
}
}
public class ButtonImageConverter : ValueConverter, IValueConverter
{
public object Convert(object value, Type targetType,
object parameter, CultureInfo culture)
{
// Use the ButtonResources helper to find the image to put on the button.
return ButtonResources.FindButton(value as String, culture).Image;
}
}
public class ButtonColourConverter : ValueConverter, IValueConverter
{
public object Convert(object value, Type targetType,
object parameter, CultureInfo culture)
{
// Use the ButtonResources helper to find the brush to use for the button.
return ButtonResources.FindButton(value as String, culture).Brush;
}
}
注意:在解决方案资源管理器中,我清除了Custom Tool
文件的ButtonResources.resx
字段。这是因为ButtonResources
类处理resgen.exe工具创建的代码所做的一切。
如果我需要本地化资源,我很确定CultureInfo
的东西会出错,但它现在有用,所以如果有问题我会解决这个问题。
除了本地化问题之外,ButtonResources
类可能会使用一些额外的代码来处理不存在命名资源的情况,从而提供可接受的默认值,或者至少是{ {1}}。同样,这不会导致问题,所以我在这里没有做任何修复。