Caliburn.Micro 如何将动作绑定到嵌套的 ViewModel 方法?
In Caliburn.Micro how to bind action to nested ViewModel method?
我有一个 WPF 应用程序,由 Caliburn.Micro 提供支持,视图模型优先方法。有一个带有 CommandBarView.xaml 和绑定 CommandBarViewModel 的命令栏类型的控件。命令栏 VM 包含许多嵌套的 VM,每个按钮控件一个,所有这些都显示一个通用界面并具有通用行为。命令栏 VM 公开它们以便它们可以从视图中绑定:
public interface IWarningButtonViewModel
{
bool IsVisible { get; }
bool CanShowWarning { get; }
void ShowWarning();
}
public class CommandBarViewModel : PropertyChangedBase
{
public IWarningButtonViewModel UserNotFoundWarning { get; private set; }
public IWarningButtonViewModel NetworkProblemWarning { get; private set; }
// ... initialization omitted for simplicity
}
这是对 CommandBarView 的一点尝试 XAML:
<Button x:Name="UserNotFoundWarning_ShowWarning"
IsEnabled="{Binding UserNotFoundWarning.CanShowWarning}">
...
<DataTrigger Binding="{Binding UserNotFoundWarning.IsVisible}" Value="True">
...
</Button>
通过这种方式,我能够成功绑定两个属性(CanShowWarning、IsVisible),但我无法将按钮 command/action 绑定到 ShowWarning 方法。
我尝试使用 deep property binding 并且它再次适用于属性,但不适用于操作。
我还尝试混合使用 cal:Model.Bind
和 cal:Message.Attach
:
<Button cal:Model.Bind="{Binding UserNotFoundWarning}"
cal:Message.Attach="[Event Click] = [Action ShowWarning]"
IsEnabled="{Binding CanShowWarning}">
...
<DataTrigger Binding="{Binding IsVisible}" Value="True">
...
</Button>
这似乎在运行时有效,但是 cal:Model.Bind 使 VS 设计器完全无法使用,UI 控件未显示。
我搜索了很多,但找不到让我也能与设计师一起工作的实际解决方案。我似乎很奇怪,我只能找到属性深度绑定的示例,而不能找到操作深度绑定的示例。
知道如何解决这个问题吗?
这是我的解决方法:
private static void EnableNestedViewModelActionBinding()
{
var baseGetTargetMethod = ActionMessage.GetTargetMethod;
ActionMessage.GetTargetMethod = (message, target) =>
{
var methodName = GetRealMethodName(message.MethodName, ref target);
if (methodName == null)
return null;
var fakeMessage = new ActionMessage { MethodName = methodName };
foreach (var p in message.Parameters)
fakeMessage.Parameters.Add(p);
return baseGetTargetMethod(fakeMessage, target);
};
var baseSetMethodBinding = ActionMessage.SetMethodBinding;
ActionMessage.SetMethodBinding = context =>
{
baseSetMethodBinding(context);
var target = context.Target;
if (target != null)
{
GetRealMethodName(context.Message.MethodName, ref target);
context.Target = target;
}
};
}
private static string GetRealMethodName(string methodName, ref object target)
{
var parts = methodName.Split('.');
var model = target;
foreach (var propName in parts.Take(parts.Length - 1))
{
if (model == null)
return null;
var prop = model.GetType().GetPropertyCaseInsensitive(propName);
if (prop == null || !prop.CanRead)
return null;
model = prop.GetValue(model);
}
target = model;
return parts.Last();
}
从您的引导程序调用 EnableNestedViewModelActionBinding()
一次,它将允许您使用通常的点分符号将操作绑定到嵌套模型的方法。例如
cal:Message.Attach="[Event Click] = [Action UserNotFoundWarning.ShowWarning]"
编辑: 请注意,如果您在运行时更改嵌套的 ViewModel 实例,这将不起作用。例如。如果您在绑定发生后将 UserNotFoundWarning
分配给新的东西 - Caliburn 仍会调用先前实例的操作。
我有一个 WPF 应用程序,由 Caliburn.Micro 提供支持,视图模型优先方法。有一个带有 CommandBarView.xaml 和绑定 CommandBarViewModel 的命令栏类型的控件。命令栏 VM 包含许多嵌套的 VM,每个按钮控件一个,所有这些都显示一个通用界面并具有通用行为。命令栏 VM 公开它们以便它们可以从视图中绑定:
public interface IWarningButtonViewModel
{
bool IsVisible { get; }
bool CanShowWarning { get; }
void ShowWarning();
}
public class CommandBarViewModel : PropertyChangedBase
{
public IWarningButtonViewModel UserNotFoundWarning { get; private set; }
public IWarningButtonViewModel NetworkProblemWarning { get; private set; }
// ... initialization omitted for simplicity
}
这是对 CommandBarView 的一点尝试 XAML:
<Button x:Name="UserNotFoundWarning_ShowWarning"
IsEnabled="{Binding UserNotFoundWarning.CanShowWarning}">
...
<DataTrigger Binding="{Binding UserNotFoundWarning.IsVisible}" Value="True">
...
</Button>
通过这种方式,我能够成功绑定两个属性(CanShowWarning、IsVisible),但我无法将按钮 command/action 绑定到 ShowWarning 方法。
我尝试使用 deep property binding 并且它再次适用于属性,但不适用于操作。
我还尝试混合使用 cal:Model.Bind
和 cal:Message.Attach
:
<Button cal:Model.Bind="{Binding UserNotFoundWarning}"
cal:Message.Attach="[Event Click] = [Action ShowWarning]"
IsEnabled="{Binding CanShowWarning}">
...
<DataTrigger Binding="{Binding IsVisible}" Value="True">
...
</Button>
这似乎在运行时有效,但是 cal:Model.Bind 使 VS 设计器完全无法使用,UI 控件未显示。
我搜索了很多,但找不到让我也能与设计师一起工作的实际解决方案。我似乎很奇怪,我只能找到属性深度绑定的示例,而不能找到操作深度绑定的示例。
知道如何解决这个问题吗?
这是我的解决方法:
private static void EnableNestedViewModelActionBinding()
{
var baseGetTargetMethod = ActionMessage.GetTargetMethod;
ActionMessage.GetTargetMethod = (message, target) =>
{
var methodName = GetRealMethodName(message.MethodName, ref target);
if (methodName == null)
return null;
var fakeMessage = new ActionMessage { MethodName = methodName };
foreach (var p in message.Parameters)
fakeMessage.Parameters.Add(p);
return baseGetTargetMethod(fakeMessage, target);
};
var baseSetMethodBinding = ActionMessage.SetMethodBinding;
ActionMessage.SetMethodBinding = context =>
{
baseSetMethodBinding(context);
var target = context.Target;
if (target != null)
{
GetRealMethodName(context.Message.MethodName, ref target);
context.Target = target;
}
};
}
private static string GetRealMethodName(string methodName, ref object target)
{
var parts = methodName.Split('.');
var model = target;
foreach (var propName in parts.Take(parts.Length - 1))
{
if (model == null)
return null;
var prop = model.GetType().GetPropertyCaseInsensitive(propName);
if (prop == null || !prop.CanRead)
return null;
model = prop.GetValue(model);
}
target = model;
return parts.Last();
}
从您的引导程序调用 EnableNestedViewModelActionBinding()
一次,它将允许您使用通常的点分符号将操作绑定到嵌套模型的方法。例如
cal:Message.Attach="[Event Click] = [Action UserNotFoundWarning.ShowWarning]"
编辑: 请注意,如果您在运行时更改嵌套的 ViewModel 实例,这将不起作用。例如。如果您在绑定发生后将 UserNotFoundWarning
分配给新的东西 - Caliburn 仍会调用先前实例的操作。