前言:

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

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. php中的一些碎的知识点

    PHP函数之可变函数,即可以通过变量的名字来调用函数,因为变量的值是可变的,所以可以通过改变一个变量来调用不同的函数 例如 function name(){     echo "name&q ...

  2. Handle详解

    首先通过一个函数启动一个服务器,只提供一个方法并返回Hello World!,当你在浏览器输入http://127.0.0.1:8080,就会看到Hello World. 对于http.ListenA ...

  3. [DB] Kafka

    介绍 一种高吞吐量的分布式发布订阅消息系统 消息类型:主体Topic(广播).队列Queue(一对一) 消息系统类型:同步消息系统.异步消息系统 常见消息产品:Redis.Kafka.JMS 术语 P ...

  4. javaWeb——jsp

    JavaBean 提供一个默认的无参构造函数 需要被序列化并且实现了 Serializable 接口 可能有一系列可读写属性 可能有一系列的 getter 或 setter 方法

  5. $(cd "$(dirname "$0")",pwd) 解析

    xx.sh 文件内容如下: #!/bin/bash BIN_FOLDER=$(cd "$(dirname "$0")";pwd) echo $BIN_FOLDE ...

  6. Linux下Shell实现服务器IP监测

    实验室有一个服务器放在机房,装的是Ubuntu Server,IP为自动分配,因此一旦IP有变化就无法远程操作,必须去机房记录新的IP.学了几天Shell之后想,是不是可以定时检测其IP的变化,一旦有 ...

  7. spark-steaming的exactly-once

    spark实时计算中会存在数据丢失和数据重复计算的场景, 在receiver收到数据且通过driver的调度executor开始计算数据的时候如果driver突然崩溃,则此时executor就会被杀掉 ...

  8. Freemaker生成复杂样式图片并无文件损坏的excel

    Freemaker生成复杂样式图片并无文件损坏的excel 参考Freemarker整合poi导出带有图片的Excel教程,优化代码实现 功能介绍:1.支持Freemarker导出Excel的所有功能 ...

  9. 安卓开发(2)—— Kotlin语言概述

    安卓开发(2)-- Kotlin语言概述 Android的官方文档都优先采用Kotlin语言了,学它来进行Android开发已经是一种大势所趋了. 这里只讲解部分的语法. 如何运行Kotlin代码 这 ...

  10. GO学习-(19) Go语言基础之网络编程

    Go语言基础之网络编程 现在我们几乎每天都在使用互联网,我们前面已经学习了如何编写Go语言程序,但是如何才能让我们的程序通过网络互相通信呢?本章我们就一起来学习下Go语言中的网络编程. 关于网络编程其 ...