前言:
最初是想解答園友“小代碼大世界 ”的問題,后來想舉一反三,將這個問題簡單剖析下,做到知其所以然,
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中也有相關問題,
翻譯如下:
- 第一種型別是一個簡單的ICommand實作,通過視圖模型(或類似的)提供,這些命令在執行時通常只是呼叫一個方法,盡管WPF并沒有為此提供一個本地實作,但許多流行的MVVM庫確實有一些ICommand介面的實作,旨在使方法呼叫變得簡單,
- 路由命令的設定是為了明確地在呼叫命令的元素和處理命令執行的元素之間造成分離,當一個路由命令被呼叫時,WPF通過可視化樹(從呼叫命令的元素開始)尋找一個可以處理該命令的元素,如果沒有找到處理程式,那么該命令將導致該按鈕被禁用,在DialogHost.OpenDialogCommand的例子中,這種情況經常發生,因為在視覺樹中沒有找到DialogHost實體,你也可以通過使用CommandTarget屬性來指定另一個命令目標,以找到RoutedCommand的處理程式,
根本原因就是“因為在視覺樹中沒有找到DialogHost實體”,這就是隱晦前提條件,
GitHub主頁的wiki中介紹了打開對話框的4種方式,分別如下:
DialogHost.OpenDialogCommand
<Button Command="{x:Static md:DialogHost.OpenDialogCommand}" />RoutedCommand 通常從一個按鈕中使用,可通過CommandParameter提供可選的內容,
DialogHost.IsOpen
<md:DialogHost IsOpen="True" />依賴屬性,可以從XAML觸發,從代碼后臺或通過系結設定,內容必須在DialogHost.DialogContent中設定,
DialogHost.Show
DialogHost.Show(viewOrModel);基于異步/等待的靜態API,可以純粹地在代碼中使用(例如從視圖模型中),內容可以直接傳遞給對話框,注意,如果你有多個視窗和多個DialogHost實體,你可以設定DialogHost.Identifier屬性,并向.Show(..)方法提供識別符號,以幫助找到所需的DialogHost,
如果有多個可能的對話主機實體,在正確的視窗顯示對話框的替代方法是使用擴展方法,
.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
轉載請註明出處,本文鏈接:https://www.uj5u.com/net/288728.html
標籤:WPF
上一篇:WPF 勾選劃線
