前言:

最初是想解答园友“小代码大世界 ”的问题,后来想举一反三,将这个问题简单剖析下,做到知其所以然。

MaterialDesignInXAML 控件库高度封装,有一些控件在使用过程中有隐晦前提条件,使用前还得多读读源码。

针对这个问题,我分步骤说明一下解决思路。

1.首先解释下“无法绑定到目标方法,因其签名或安全透明度与委托类型的签名或安全透明度不兼容”异常。字面看是提示有属性绑定失败,而且提到是委托类型,由此需检查哪些属性是绑定到委托类型的。

DialogOpenedAttached、DialogClosingAttached是DialogHost控件的两个附件属性,官方示例是绑定到CombinedDialogOpenedEventHandler、CombinedDialogClosingEventHandler上,这两个事件处理函数是需要我们在后置代码中去定义的。我们可以在这两个事件处理函数中添加一些自定义逻辑。

public void CombinedDialogOpenedEventHandler(object sender, DialogOpenedEventArgs eventArgs)
{
CombinedCalendar.SelectedDate = _mainViewModel.Date;
CombinedClock.Time = _mainViewModel.Time;
} public void CombinedDialogClosingEventHandler(object sender, DialogClosingEventArgs eventArgs)
{
if (Convert.ToBoolean(Convert.ToUInt16(eventArgs.Parameter)) && CombinedCalendar.SelectedDate is DateTime selectedDate)
{
DateTime combined = selectedDate.AddSeconds(CombinedClock.Time.TimeOfDay.TotalSeconds);
_mainViewModel.Time = combined;
_mainViewModel.Date = combined;
}
}

编译不再报错,这样最直接的问题就解决了。

2.接下来还会遇到一个问题,就是弹出时间选择器对话框的按钮是禁用状态。

这个问题在控件GitHub主页的wiki中有详细解答,issues中也有相关问题。

翻译如下:

  1. 第一种类型是一个简单的ICommand实现,通过视图模型(或类似的)提供。这些命令在执行时通常只是调用一个方法。尽管WPF并没有为此提供一个本地实现,但许多流行的MVVM库确实有一些ICommand接口的实现,旨在使方法调用变得简单。
  2. 路由命令的设置是为了明确地在调用命令的元素和处理命令执行的元素之间造成分离。当一个路由命令被调用时,WPF通过可视化树(从调用命令的元素开始)寻找一个可以处理该命令的元素。如果没有找到处理程序,那么该命令将导致该按钮被禁用。在DialogHost.OpenDialogCommand的例子中,这种情况经常发生,因为在视觉树中没有找到DialogHost实例。你也可以通过使用CommandTarget属性来指定另一个命令目标,以找到RoutedCommand的处理程序。

根本原因就是“因为在视觉树中没有找到DialogHost实例”。这就是隐晦前提条件。

GitHub主页的wiki中介绍了打开对话框的4种方式,分别如下:

  1. DialogHost.OpenDialogCommand

    <Button Command="{x:Static md:DialogHost.OpenDialogCommand}" />

    RoutedCommand 通常从一个按钮中使用,可通过CommandParameter提供可选的内容。

  2. DialogHost.IsOpen

    <md:DialogHost IsOpen="True" />

    依赖属性,可以从XAML触发,从代码后台或通过绑定设置。内容必须在DialogHost.DialogContent中设置。

  3. DialogHost.Show

    DialogHost.Show(viewOrModel);

    基于异步/等待的静态API,可以纯粹地在代码中使用(例如从视图模型中)。内容可以直接传递给对话框。注意,如果你有多个窗口和多个DialogHost实例,你可以设置DialogHost.Identifier属性,并向.Show(..)方法提供标识符,以帮助找到所需的DialogHost。

    如果有多个可能的对话主机实例,在正确的窗口显示对话框的替代方法是使用扩展方法。

  4. .Show() 扩展方法

    该方法同时扩展了Window和DepedencyObject,将尝试定位最合适的DialogHost来显示对话框。

但文档还漏了一种很实用也很常见的方式,官方的示例就是这种用法。将 MainWindow 的根容器定义为 DialogHost。

<materialDesign:DialogHost DialogTheme="Inherit"
Identifier="RootDialog">
<!--Content-->
</materialDesign:DialogHost>

这样在视觉树中始终能找到DialogHost实例,对于实现弹框全局模态效果,这种方式值得推荐。

代码如下:

<materialDesign:DialogHost DialogTheme="Inherit"
Identifier="RootDialog">
<Grid>
<StackPanel HorizontalAlignment="Center"
VerticalAlignment="Center"
Orientation="Horizontal">
<TextBlock VerticalAlignment="Center"
FontSize="24"
Text="{Binding Date, StringFormat={}{0:yyyy-MM-dd HH:mm:ss}}" />
<Button Margin="8 0 0 0"
materialDesign:DialogHost.DialogClosingAttached="CombinedDialogClosingEventHandler"
materialDesign:DialogHost.DialogOpenedAttached="CombinedDialogOpenedEventHandler"
Command="{x:Static materialDesign:DialogHost.OpenDialogCommand}"
Content="...">
<Button.CommandParameter>
<Grid Margin="-1">
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<StackPanel Grid.Row="0"
Orientation="Horizontal">
<Calendar x:Name="CombinedCalendar"
Margin="-1 -4 -1 0" />
<materialDesign:Clock x:Name="CombinedClock"
DisplayAutomation="CycleWithSeconds"
Is24Hours="True" />
</StackPanel>
<StackPanel Grid.Row="1"
Margin="8"
HorizontalAlignment="Right"
Orientation="Horizontal">
<Button Command="{x:Static materialDesign:DialogHost.CloseDialogCommand}"
CommandParameter="0"
Content="CANCEL"
Style="{DynamicResource MaterialDesignFlatButton}" />
<Button Command="{x:Static materialDesign:DialogHost.CloseDialogCommand}"
CommandParameter="1"
Content="OK"
Style="{DynamicResource MaterialDesignFlatButton}" />
</StackPanel>
</Grid>
</Button.CommandParameter>
</Button>
</StackPanel>
</Grid>
</materialDesign:DialogHost>
3.除了定义DialogHost作为根容器,还可以通过指定CommandTarget的来解决。
<Grid>
<StackPanel HorizontalAlignment="Center"
VerticalAlignment="Center"
Orientation="Horizontal">
<TextBlock VerticalAlignment="Center"
FontSize="24"
Text="{Binding Date, StringFormat={}{0:yyyy-MM-dd HH:mm:ss}}" />
<Button Margin="8 0 0 0"
materialDesign:DialogHost.DialogClosingAttached="CombinedDialogClosingEventHandler"
materialDesign:DialogHost.DialogOpenedAttached="CombinedDialogOpenedEventHandler"
Command="{x:Static materialDesign:DialogHost.OpenDialogCommand}"
CommandTarget="{Binding ElementName=DatePickerDialogHost}"
Content="...">
<Button.CommandParameter>
<Grid Margin="-1">
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions> <StackPanel Grid.Row="0"
Orientation="Horizontal">
<Calendar x:Name="CombinedCalendar"
Margin="-1 -4 -1 0" /> <materialDesign:Clock x:Name="CombinedClock"
DisplayAutomation="CycleWithSeconds"
Is24Hours="True" />
</StackPanel> <StackPanel Grid.Row="1"
Margin="8"
HorizontalAlignment="Right"
Orientation="Horizontal">
<Button Command="{x:Static materialDesign:DialogHost.CloseDialogCommand}"
CommandParameter="0"
Content="CANCEL"
Style="{DynamicResource MaterialDesignFlatButton}" /> <Button Command="{x:Static materialDesign:DialogHost.CloseDialogCommand}"
CommandParameter="1"
Content="OK"
Style="{DynamicResource MaterialDesignFlatButton}" />
</StackPanel>
</Grid>
</Button.CommandParameter>
</Button>
</StackPanel> <materialDesign:DialogHost x:Name="DatePickerDialogHost">
</materialDesign:DialogHost>
</Grid>

这种方式的Content部分是通过CommandParameter传递给Command的。下面的源代码一目了然。

CommandBindings.Add(new CommandBinding(OpenDialogCommand, OpenDialogHandler));

private void OpenDialogHandler(object sender, ExecutedRoutedEventArgs executedRoutedEventArgs)
{
if (executedRoutedEventArgs.Handled) return;
if (executedRoutedEventArgs.OriginalSource is DependencyObject dependencyObject)
{
_attachedDialogOpenedEventHandler = GetDialogOpenedAttached(dependencyObject);
_attachedDialogClosingEventHandler = GetDialogClosingAttached(dependencyObject);
}
if (executedRoutedEventArgs.Parameter != null)
{
AssertTargetableContent();
if (_popupContentControl != null)
{
_popupContentControl.DataContext = OpenDialogCommandDataContextSource switch
{
DialogHostOpenDialogCommandDataContextSource.SenderElement
=> (executedRoutedEventArgs.OriginalSource as FrameworkElement)?.DataContext,
DialogHostOpenDialogCommandDataContextSource.DialogHostInstance => DataContext,
DialogHostOpenDialogCommandDataContextSource.None => null,
_ => throw new ArgumentOutOfRangeException(),
};
}
DialogContent = executedRoutedEventArgs.Parameter;
}
SetCurrentValue(IsOpenProperty, true);
executedRoutedEventArgs.Handled = true;
}
4.如果不想通过CommandParameter传递内容参数,还可以直接在DialogContent定义内容部分。这是使用Dialog控件的常见方式。
<Grid>
<materialDesign:DialogHost>
<materialDesign:DialogHost.DialogContent>
<Grid Margin="-1">
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions> <StackPanel Grid.Row="0"
Orientation="Horizontal">
<Calendar x:Name="CombinedCalendar"
Margin="-1 -4 -1 0" /> <materialDesign:Clock x:Name="CombinedClock"
DisplayAutomation="CycleWithSeconds"
Is24Hours="True" />
</StackPanel> <StackPanel Grid.Row="1"
Margin="8"
HorizontalAlignment="Right"
Orientation="Horizontal">
<Button Command="{x:Static materialDesign:DialogHost.CloseDialogCommand}"
CommandParameter="0"
Content="CANCEL"
Style="{DynamicResource MaterialDesignFlatButton}" /> <Button Command="{x:Static materialDesign:DialogHost.CloseDialogCommand}"
CommandParameter="1"
Content="OK"
Style="{DynamicResource MaterialDesignFlatButton}" />
</StackPanel>
</Grid>
</materialDesign:DialogHost.DialogContent> <StackPanel HorizontalAlignment="Center"
VerticalAlignment="Center"
Orientation="Horizontal">
<TextBlock VerticalAlignment="Center"
FontSize="24"
Text="{Binding Date, StringFormat={}{0:yyyy-MM-dd HH:mm:ss}}" />
<Button Margin="8 0 0 0"
materialDesign:DialogHost.DialogClosingAttached="CombinedDialogClosingEventHandler"
materialDesign:DialogHost.DialogOpenedAttached="CombinedDialogOpenedEventHandler"
Command="{x:Static materialDesign:DialogHost.OpenDialogCommand}"
CommandTarget="{Binding ElementName=DatePickerDialogHost}"
Content="..." />
</StackPanel>
</materialDesign:DialogHost>
</Grid>

源码下载:网盘 Github

MaterialDesignInXamlToolkit“无法绑定到目标方法,因其签名或安全透明度与委托类型的签名或安全透明度不兼容”异常的解决思路的更多相关文章

  1. Spring AOP中使用args表达式访问目标方法的参数

    Spring AOP 的使用过程理解 首先,aop的使用场景介绍: 1.处理一些通用的非功能性的需求,不影响业务流程,比如说打印日志.性能统计.推送消息等: 2.aop无法拦截static.final ...

  2. jquery 绑定事件的方法

    jQuery中提供了四种绑定事件的方法,分别是bind.live.delegate.on,对应的解除监听的函数分别是unbind.die.undelegate.off: 一.on()方法(首选方法) ...

  3. JavaScript绑定事件的方法[3种]

    在JavaScript中,有三种常用的绑定事件的方法: 在DOM元素中直接绑定: 在JavaScript代码中绑定: 绑定事件监听函数. 一. 在DOM元素中直接绑定 这里的DOM元素,可以理解为HT ...

  4. GridView等表格模板列绑定数据的方法

    //绑定GridView每一行中的CheckBoxList protected void GridView1_RowDataBound(object sender, GridViewRowEventA ...

  5. elementui command绑定变量对象方法

    command绑定变量对象方法 使用v-bind : command绑定 简写 :command

  6. Spring Aop 修改目标方法参数和返回值

    一.新建注解 @Target({ElementType.METHOD, ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) @Document ...

  7. Android studio button 按钮 四种绑定事件的方法

    package com.geli_2.sujie.sujiegeili2testbutton; import android.os.Bundle; import android.support.v7. ...

  8. AOP实现拦截对象以及获取切入目标方法和注解

    AOP实现拦截对象以及获取切入目标方法和注解 一.JoinPoint是什么? AspectJ使用org.aspectj.lang.JoinPoint接口表示目标类连接点对象,如果是环绕增强时,使用 o ...

  9. SpringMVC系列(五)使用 Serlvet 原生的 API 作为目标方法的参数

    SpringMVC的Handler方法可以接受哪些 ServletAPI 类型的参数 • HttpServletRequest• HttpServletResponse• HttpSession• j ...

随机推荐

  1. Beta——事后分析

    事后总结 NameNotFound 团队 项目 内容 北航-2020-软件工程(春季学期) 班级博客 要求 Beta事后分析 课程目标 通过团队合作完成一个软件项目的开发 会议截图 一.设想和目标 软 ...

  2. input type

    input的type有: text 文本输入 password密码输入 file选择文件 radio单选按钮 checkbox复选按钮 submit对应form的action按钮 button 普通按 ...

  3. 在python的class中的,self到底是什么?

    答案:self可以理解为一个字典变量,内部存的就是对象的数据属性.如:{'name':'zhang','age':'18'}就是这些. 注意只有数据属性,并没有创建新的类的方法.  类-----> ...

  4. MySQL报错ERROR 1436 (HY000): Thread stack overrun:

    今天搭私服的时候,卡在角色创建画面,日志报错如上. 这是MySQL报错ERROR 1436 (HY000): Thread stack overrun:   修改方法 vim /etc/my.cnf ...

  5. ltp 测试流程及测试脚本分析

    LTP介绍 (2011-03-25 18:03:53) 转载▼ 标签: ltp linux 压力测试 杂谈 分类: linux测试 LTP介绍 一.LTP介绍1.简介LTP(Linux Test Pr ...

  6. Jsoup_Select 选择器

    Jsoup_Select 选择器 一,概述 可直接解析某个 URL 地址.HTML 文本内容.它提供了一套非常省力的 API,可通过 DOM,CSS 以及类似于 jQuery 的操作方法来取出和操作数 ...

  7. 『动善时』JMeter基础 — 27、通过JMeter函数助手实现参数化

    目录 1.测试计划中的元件 2.数据文件内容 3.函数助手配置 (1)函数助手的打开方式 (2)函数助手界面介绍 (3)编辑后的函数助手界面 4.HTTP请求组件内容 5.线程组元件内容 6.脚本运行 ...

  8. SPI接口在LCD上的应用

    ​小分辨率的LCD,比如QQVGA,QCIF,QVGA等,广泛应用于功能手机和穿戴设备(比如手表)上.这类小分辨率的LCD,除了支持并行接口(比如i80),一般也会支持串行接口.在实际产品中广泛运用的 ...

  9. Python小白的数学建模课-A1.2021年数维杯C题(运动会优化比赛模式探索)探讨

    Python小白的数学建模课 A1-2021年数维杯C题(运动会优化比赛模式探索)探讨. 运动会优化比赛模式问题,是公平分配问题 『Python小白的数学建模课 @ Youcans』带你从数模小白成为 ...

  10. F5 BIG-IQ 8.0 发布 - 借助一个统一的管理平台,管理您的所有 BIG-IP 设备和服务

    针对完整可见性和控制的 BIG-IQ 集中式管理 BIG-IP 设备和服务的端到端控制 高效管理(编排.可见性和合规性)依赖于跨本地和云部署中始终如一的应用服务和安全策略.利用独立统一的管理平台实现对 ...