namespace
Mersoft.Mvvm.MarkupExtensions
{
///
<summary>
///
用于处理 绑定代表资源键 (key) 的变量 业务的标记扩展类
///
markup extension to allow binding to resourceKey in general case.
///
https://stackoverflow.com/questions/20564862/binding-to-resource-key-wpf
///
</summary>
///
<example>
///
<code>
///
(Image Source="{local:ResourceBinding ImageResourceKey}"/>
///
</code>
///
</example>
public
class
ResourceBinding
:
MarkupExtension
{
#
region
Helper properties
public
static
object
GetResourceBindingKeyHelper
(
DependencyObject obj
)
{
return
(
object
)obj.GetValue(ResourceBindingKeyHelperProperty);
}
public
static
void
SetResourceBindingKeyHelper
(
DependencyObject obj,
object
value
)
{
obj.SetValue(ResourceBindingKeyHelperProperty,
value
);
}
// Using a DependencyProperty as the backing store for ResourceBindingKeyHelper. This enables animation, styling, binding, etc...
public
static
readonly
DependencyProperty ResourceBindingKeyHelperProperty =
DependencyProperty.RegisterAttached(
"ResourceBindingKeyHelper"
,
typeof
(
object
),
typeof
(ResourceBinding),
new
PropertyMetadata(
null
, ResourceKeyChanged));
static
void
ResourceKeyChanged
(
DependencyObject d, DependencyPropertyChangedEventArgs e
)
{
var
target = d
as
FrameworkElement;
var
newVal = e.
NewValue
as
Tuple<
object
, DependencyProperty>
if
(
target ==
null
|| newVal ==
null
)
return
;
var
dp = newVal.Item2;
if
(newVal.Item1 ==
null
)
{
target.SetValue(dp, dp.GetMetadata(target).DefaultValue);
return
;
}
target.SetResourceReference(dp, newVal.Item1);
}
#
endregion
public
ResourceBinding
(
)
{
}
public
ResourceBinding
(
string
path
)
{
Path =
new
PropertyPath(path);
}
public
override
object
ProvideValue
(
IServiceProvider serviceProvider
)
{
var
provideValueTargetService = (IProvideValueTarget)serviceProvider.GetService(
typeof
(IProvideValueTarget));
if
(provideValueTargetService ==
null
)
return
null
;
if
(provideValueTargetService.TargetObject !=
null
&&
provideValueTargetService.TargetObject.GetType.FullName ==
"System.Windows.SharedDp"
)
return
this
;
var
targetObject = provideValueTargetService.TargetObject
as
FrameworkElement;
var
targetProperty = provideValueTargetService.TargetProperty
as
DependencyProperty;
if
(targetObject ==
null
|| targetProperty ==
null
)
return
null
;
#
region
binding
Binding binding =
new
Binding
{
Path = Path,
XPath = XPath,
Mode = Mode,
UpdateSourceTrigger = UpdateSourceTrigger,
Converter = Converter,
ConverterParameter = ConverterParameter,
ConverterCulture = ConverterCulture,
FallbackValue = FallbackValue
};
if
(RelativeSource !=
null
)
binding.RelativeSource = RelativeSource;
if
(ElementName !=
null
)
binding.ElementName = ElementName;
if
(Source !=
null
)
binding.Source = Source;
#
endregion
var
multiBinding =
new
MultiBinding
{
Converter = HelperConverter.Current,
ConverterParameter = targetProperty
};
multiBinding.Bindings.Add(binding);
multiBinding.NotifyOnSourceUpdated =
true
;
targetObject.SetBinding(ResourceBindingKeyHelperProperty, multiBinding);
return
null
;
}
#
region
Binding Members
///
<summary>
///
The source path (for CLR bindings).
///
</summary>
public
object
Source {
get
;
set
; }
///
<summary>
///
The source path (for CLR bindings).
///
</summary>
public
PropertyPath Path {
get
;
set
; }
///
<summary>
///
The XPath path (for XML bindings).
///
</summary>
[
DefaultValue(null)
]
public
string
XPath {
get
;
set
; }
///
<summary>
///
Binding mode
///
</summary>
[
DefaultValue(BindingMode.Default)
]
public
BindingMode Mode {
get
;
set
; }
///
<summary>
///
Update type
///
</summary>
[
DefaultValue(UpdateSourceTrigger.Default)
]
public
UpdateSourceTrigger UpdateSourceTrigger {
get
;
set
; }
///
<summary>
///
The Converter to apply
///
</summary>
[
DefaultValue(null)
]
public
IValueConverter Converter {
get
;
set
; }
///
<summary>
///
The parameter to pass to converter.
///
</summary>
///
<value>
</value>
[
DefaultValue(null)
]
public
object
ConverterParameter {
get
;
set
; }
///
<summary>
///
Culture in which to evaluate the converter
///
</summary>
[
DefaultValue(null)
]
[
TypeConverter(typeof(System.Windows.CultureInfoIetfLanguageTagConverter))
]
public
CultureInfo ConverterCulture {
get
;
set
; }
///
<summary>
///
Deion of the object to use as the source, relative to the target element.
///
</summary>
[
DefaultValue(null)
]
public
RelativeSource RelativeSource {
get
;
set
; }
///
<summary>
///
Name of the element to use as the source
///
</summary>
[
DefaultValue(null)
]
public
string
ElementName {
get
;
set
; }
#
endregion
#
region
BindingBase Members
///
<summary>
///
Value to use when source cannot provide a value
///
</summary>
///
<remarks>
///
Initialized to DependencyProperty.UnsetValue; if FallbackValue is not set, BindingExpression
///
will return target property's default when Binding cannot get a real value.
///
</remarks>
public
object
FallbackValue {
get
;
set
; }
#
endregion
#
region
Nested types
private
class
HelperConverter
:
IMultiValueConverter
{
public
static
readonly
HelperConverter Current =
new
HelperConverter;
public
object
Convert
(
object
[] values, Type targetType,
object
parameter, CultureInfo culture
)
{
return
Tuple.Create(values[
0
], (DependencyProperty)parameter);
}
public
object
[]
ConvertBack
(
object
value
, Type[] targetTypes,
object
parameter, CultureInfo culture
)
{
throw
new
NotImplementedException;
}
}
#
endregion
}
}
主要就是继承 MarkupExtension 并重写 ProvideValue 方法,具体的本人也没怎么研究,就先不说了,大家感兴趣可以自己查一查。这里直接拿来使用,可以达到动态绑定资源 key 的目的。
如果使用的是普通的 Binding,则只能显示原始值:
最后来看看中英文
切换
,当然,如果有其它语言,也是一样可以切换的。
首先是移除现有语言资源的方法:
///
<summary>
///
语言名称列表
///
</summary>
private
readonly
List<
string
> _LangKeys =
new
List<
string
> {
"en-us"
,
"zh-cn"
};
///
<summary>
///
移除语言资源
///
</summary>
///
<param name="removeKeyList">
需要移除的资源中包含的 key 的列表,默认为空,为空移除所有的
</param>
private
void
RemoveLangThemes
(
List<
string
> removeKeyList =
null
)
{
if
(removeKeyList ==
null
)
{
removeKeyList = _LangKeys;
}
var
rd = Application.Current.Resources;
List<ResourceDictionary> removeList =
new
List<ResourceDictionary>;
foreach
(
var
dictionary
in
rd.MergedDictionaries)
{
// 判断是否是对应的语言资源文件;
bool
isExists = removeKeyList.Exists(x => dictionary.Contains(
"LangName"
) && dictionary[
"LangName"
]+
""
== x);
if
(isExists)
{
removeList.Add(dictionary);
}
}
foreach
(
var
removeResource
in
removeList)
{
rd.MergedDictionaries.Remove(removeResource);
}
}
主要是对 Application.Current.Resources.MergedDictionaries 进行操作,移除有 LangName 键,且值为对应语言代号的资源字典。
然后是应用对应语言资源的方法及调用:
///
<summary>
///
应用语言
///
</summary>
///
<param name="packUriTemplate">
资源路径模板,形如:"/WPFPractice;component/Resources/Language/{0}.xaml"
</param>
///
<param name="langName">
语言名称,形如:"zh-cn"
</param>
private
void
ApplyLanguage
(
string
packUriTemplate,
string
langName =
"zh-cn"
)
{
var
rd = Application.Current.Resources;
//RemoveLangThemes;
var
packUri =
string
.Format(packUriTemplate, langName);
RemoveLangThemes(
new
List<
string
> { langName });
// 将资源加载在最后,优先使用;
rd.MergedDictionaries.Add((ResourceDictionary)Application.LoadComponent(
new
Uri(packUri, UriKind.Relative)));
}
///
<summary>
///
语言资源路径模板字符串
///
</summary>
private
string
_LangResourceUriTemplate =
"/WPFPractice;component/Resources/Language/{0}.xaml"
;
///
<summary>
///
命令方法赋值(在构造方法中调用)
///
</summary>
private
void
SetCommandMethod
(
)
{
SwitchCnCmd ??=
new
RelayCommand(o =>
true
,
async
o =>
{
ApplyLanguage(_LangResourceUriTemplate,
"zh-cn"
);
});
SwitchEnCmd ??=
new
RelayCommand(o =>
true
,
async
o =>
{
ApplyLanguage(_LangResourceUriTemplate,
"en-us"
);
});
}
逻辑就是,先移除要切换到的语言资源的已存在的实例,然后将新的实例放在最后,以达到比其它语言资源(如果有的话)更高优先级的目的。
WPF
【翻译】WPF 中附加行为的介绍 Introduction to Attached Behaviors in WPF
WPF 使用 Expression Design 画图导出及使用 Path 画图
WPF MVVM 弹框之等待框
解决 WPF 绑定集合后数据变动界面却不更新的问题(使用 ObservableCollection)
WPF 消息框 TextBox 绑定新数据时让光标和滚动条跳到最下面
真・WPF 按钮拖动和调整大小
WPF MVVM 模式下的弹窗
WPF 让一组 Button 实现 RadioButton 的当前样式效果
WPF 原生绑定和命令功能使用指南
WPF 用户控件 的 自定义 依赖属性 在 MVVM 模式下的使用备忘
在WPF的MVVM模式中使用OCX组件
返回搜狐,查看更多