Skip to content

Commit 4fc5e9d

Browse files
committed
improve control how data context can be applied to popups
1 parent 18d6308 commit 4fc5e9d

File tree

2 files changed

+63
-18
lines changed

2 files changed

+63
-18
lines changed

MaterialDesignThemes.Wpf/DialogHost.cs

Lines changed: 58 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -12,12 +12,35 @@
1212

1313
namespace MaterialDesignThemes.Wpf
1414
{
15+
/// <summary>
16+
/// Defines how a data context is sourced for a dialog if a <see cref="FrameworkElement"/>
17+
/// is passed as the command parameter when using <see cref="DialogHost.OpenDialogCommand"/>.
18+
/// </summary>
19+
public enum DialogHostOpenDialogCommandDataContextSource
20+
{
21+
/// <summary>
22+
/// The data context from the sender element (typically a <see cref="Button"/>)
23+
/// is applied to the content.
24+
/// </summary>
25+
SenderElement,
26+
/// <summary>
27+
/// The data context from the <see cref="DialogHost"/> is applied to the content.
28+
/// </summary>
29+
DialogHostInstance,
30+
/// <summary>
31+
/// The data context is explicitly set to <c>null</c>.
32+
/// </summary>
33+
None
34+
}
35+
1536
[TemplatePart(Name = PopupPartName, Type = typeof(Popup))]
37+
[TemplatePart(Name = PopupPartName, Type = typeof(ContentControl))]
1638
[TemplateVisualState(GroupName = "PopupStates", Name = OpenStateName)]
1739
[TemplateVisualState(GroupName = "PopupStates", Name = ClosedStateName)]
1840
public class DialogHost : ContentControl
1941
{
2042
public const string PopupPartName = "PART_Popup";
43+
public const string PopupContentPartName = "PART_PopupContentElement";
2144
public const string OpenStateName = "Open";
2245
public const string ClosedStateName = "Closed";
2346

@@ -37,6 +60,7 @@ public class DialogHost : ContentControl
3760
private DialogClosingEventHandler _asyncShowClosingEventHandler;
3861

3962
private Popup _popup;
63+
private ContentControl _popupContentControl;
4064
private DialogSession _session;
4165
private DialogOpenedEventHandler _attachedDialogOpenedEventHandler;
4266
private DialogClosingEventHandler _attachedDialogClosingEventHandler;
@@ -276,9 +300,23 @@ public string DialogContentStringFormat
276300
set { SetValue(DialogContentStringFormatProperty, value); }
277301
}
278302

303+
public static readonly DependencyProperty OpenDialogCommandDataContextSourceProperty = DependencyProperty.Register(
304+
"OpenDialogCommandDataContextSource", typeof (DialogHostOpenDialogCommandDataContextSource), typeof (DialogHost), new PropertyMetadata(default(DialogHostOpenDialogCommandDataContextSource)));
305+
306+
/// <summary>
307+
/// Defines how a data context is sourced for a dialog if a <see cref="FrameworkElement"/>
308+
/// is passed as the command parameter when using <see cref="DialogHost.OpenDialogCommand"/>.
309+
/// </summary>
310+
public DialogHostOpenDialogCommandDataContextSource OpenDialogCommandDataContextSource
311+
{
312+
get { return (DialogHostOpenDialogCommandDataContextSource) GetValue(OpenDialogCommandDataContextSourceProperty); }
313+
set { SetValue(OpenDialogCommandDataContextSourceProperty, value); }
314+
}
315+
279316
public override void OnApplyTemplate()
280317
{
281-
_popup = GetTemplateChild(PopupPartName) as Popup;
318+
_popup = GetTemplateChild(PopupPartName) as Popup;
319+
_popupContentControl = GetTemplateChild(PopupContentPartName) as ContentControl;
282320

283321
VisualStateManager.GoToState(this, SelectState(), false);
284322

@@ -373,7 +411,7 @@ public static DialogClosingEventHandler GetDialogClosingAttached(DependencyObjec
373411
}
374412

375413
public static readonly DependencyProperty DialogClosingCallbackProperty = DependencyProperty.Register(
376-
"DialogClosingCallback", typeof (DialogClosingEventHandler), typeof (DialogHost), new PropertyMetadata(default(DialogClosingEventHandler)));
414+
"DialogClosingCallback", typeof (DialogClosingEventHandler), typeof (DialogHost), new PropertyMetadata(default(DialogClosingEventHandler)));
377415

378416
/// <summary>
379417
/// Callback fired when the <see cref="DialogClosing"/> event is fired, allowing the event to be processed from a binding/view model.
@@ -438,19 +476,26 @@ private void OpenDialogHandler(object sender, ExecutedRoutedEventArgs executedRo
438476
{
439477
AssertTargetableContent();
440478

441-
//TODO enhancement: make the following configurable, so that the data context can be pulled from the dialog host if desired.
442-
// (leave the current behaviour as the default; most developers will find this logical, as the data context will "inherit" from button containing the content)
443-
444-
var contentElement = executedRoutedEventArgs.Parameter as FrameworkElement;
445-
var senderElement = executedRoutedEventArgs.OriginalSource as FrameworkElement;
446-
if (contentElement != null && senderElement != null && contentElement.DataContext == null && BindingOperations.GetBindingExpression(contentElement, DataContextProperty) == null)
479+
if (_popupContentControl != null)
447480
{
448-
DialogContent = executedRoutedEventArgs.Parameter;
449-
contentElement.SetCurrentValue(DataContextProperty, senderElement.DataContext);
481+
switch (OpenDialogCommandDataContextSource)
482+
{
483+
case DialogHostOpenDialogCommandDataContextSource.SenderElement:
484+
_popupContentControl.DataContext =
485+
(executedRoutedEventArgs.Parameter as FrameworkElement)?.DataContext;
486+
break;
487+
case DialogHostOpenDialogCommandDataContextSource.DialogHostInstance:
488+
_popupContentControl.DataContext = DataContext;
489+
break;
490+
case DialogHostOpenDialogCommandDataContextSource.None:
491+
_popupContentControl.DataContext = null;
492+
break;
493+
default:
494+
throw new ArgumentOutOfRangeException();
495+
}
450496
}
451-
else
452-
DialogContent = executedRoutedEventArgs.Parameter;
453497

498+
DialogContent = executedRoutedEventArgs.Parameter;
454499
}
455500

456501
SetCurrentValue(IsOpenProperty, true);
@@ -462,7 +507,7 @@ private void CloseDialogHandler(object sender, ExecutedRoutedEventArgs executedR
462507
{
463508
if (executedRoutedEventArgs.Handled) return;
464509

465-
Close(executedRoutedEventArgs.Parameter);
510+
Close(executedRoutedEventArgs.Parameter);
466511

467512
executedRoutedEventArgs.Handled = true;
468513
}

MaterialDesignThemes.Wpf/Themes/Generic.xaml

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -434,7 +434,7 @@
434434
</EasingDoubleKeyFrame.EasingFunction>
435435
</EasingDoubleKeyFrame>
436436
</DoubleAnimationUsingKeyFrames>
437-
<DoubleAnimationUsingKeyFrames Storyboard.TargetName="Card" Storyboard.TargetProperty="Opacity">
437+
<DoubleAnimationUsingKeyFrames Storyboard.TargetName="PART_PopupContentElement" Storyboard.TargetProperty="Opacity">
438438
<EasingDoubleKeyFrame Value="0" KeyTime="0" />
439439
<EasingDoubleKeyFrame Value="1" KeyTime="0:0:0.3">
440440
<EasingDoubleKeyFrame.EasingFunction>
@@ -474,7 +474,7 @@
474474
</EasingDoubleKeyFrame.EasingFunction>
475475
</EasingDoubleKeyFrame>
476476
</DoubleAnimationUsingKeyFrames>
477-
<DoubleAnimationUsingKeyFrames Storyboard.TargetName="Card" Storyboard.TargetProperty="Opacity">
477+
<DoubleAnimationUsingKeyFrames Storyboard.TargetName="PART_PopupContentElement" Storyboard.TargetProperty="Opacity">
478478
<EasingDoubleKeyFrame Value="1" KeyTime="0" />
479479
<EasingDoubleKeyFrame Value="1" KeyTime="0:0:0.18" />
480480
<EasingDoubleKeyFrame Value="0" KeyTime="0:0:0.3">
@@ -511,7 +511,7 @@
511511
</BooleanAnimationUsingKeyFrames>
512512
<DoubleAnimation Storyboard.TargetName="ContentGrid" Storyboard.TargetProperty="Opacity"
513513
To=".56" />
514-
<DoubleAnimation Storyboard.TargetName="Card" Storyboard.TargetProperty="Opacity"
514+
<DoubleAnimation Storyboard.TargetName="PART_PopupContentElement" Storyboard.TargetProperty="Opacity"
515515
To="1" />
516516
<DoubleAnimation Storyboard.TargetName="CardScaleTransform" Storyboard.TargetProperty="ScaleX"
517517
To="1" />
@@ -543,7 +543,8 @@
543543
</ResourceDictionary.MergedDictionaries>
544544
</ResourceDictionary>
545545
</Popup.Resources>
546-
<local:Card Margin="22"
546+
<local:Card x:Name="PART_PopupContentElement"
547+
Margin="22"
547548
local:ShadowAssist.ShadowDepth="Depth5"
548549
UniformCornerRadius="4"
549550
TextElement.Foreground="{DynamicResource MaterialDesignBody}"
@@ -554,7 +555,6 @@
554555
Foreground="{DynamicResource MaterialDesignBody}"
555556
FontFamily="pack://application:,,,/MaterialDesignThemes.Wpf;component/Resources/Roboto/#Roboto"
556557
IsTabStop="False"
557-
x:Name="Card"
558558
Opacity="0"
559559
RenderTransformOrigin=".5,.5"
560560
Content="{TemplateBinding DialogContent}"

0 commit comments

Comments
 (0)