replace v6 with experimental v7 code

This commit is contained in:
stax76
2023-10-24 11:17:45 +02:00
parent fb27bb8727
commit 5706d7b66d
212 changed files with 15014 additions and 12173 deletions

View File

@@ -0,0 +1,226 @@
<Window x:Class="MpvNet.Windows.WPF.MsgBox.MessageBoxEx"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
Title="{Binding Path=MessageTitle}"
MinHeight="100"
MaxHeight="650"
MinWidth="200"
MaxWidth="{Binding Path=MaxFormWidth}"
Background="{Binding Path=MessageBackground}"
WindowStartupLocation="CenterScreen"
ShowInTaskbar="False"
WindowStyle="SingleBorderWindow"
ResizeMode="NoResize"
SizeToContent="WidthAndHeight"
SizeChanged="NotifiableWindow_SizeChanged"
Loaded="Window_Loaded"
Closing="Window_Closing"
Height="Auto"
Width="Auto"
SourceInitialized="Window_SourceInitialized"
>
<Window.Resources>
<Style x:Key="GroupBoxExpanderToggleButtonStyle" TargetType="{x:Type ToggleButton}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ToggleButton}">
<Grid SnapsToDevicePixels="False" Background="Transparent">
<Ellipse HorizontalAlignment="Center" x:Name="circle" VerticalAlignment="Center" Width="15" Height="15"
Fill="{DynamicResource ButtonNormalBackgroundFill}" Stroke="{Binding Path=MessageForeground}"/>
<Ellipse Visibility="Hidden" HorizontalAlignment="Center" x:Name="shadow" VerticalAlignment="Center" Width="13" Height="13"
Fill="{DynamicResource ExpanderShadowFill}"/>
<Path SnapsToDevicePixels="false" x:Name="arrow" VerticalAlignment="Center" HorizontalAlignment="Center"
Stroke="{Binding Path=MessageForeground}" StrokeThickness="2" Data="M1,1 L4,4 7,1" />
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="IsChecked" Value="true">
<Setter Property="Data" TargetName="arrow" Value="M 1,4 L 4,1 L 7,4"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<SolidColorBrush x:Key="GroupBoxBorderBrush" Color="#D0D0BF"/>
<SolidColorBrush x:Key="GroupBoxHeaderBrush" Color="White"/>
<BorderGapMaskConverter x:Key="BorderGapMaskConverter"/>
<Style x:Key="linkCursor" TargetType="{x:Type TextBlock}" >
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True" >
<Setter Property="Cursor" Value="Hand" />
</Trigger>
</Style.Triggers>
</Style>
</Window.Resources>
<Grid >
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid x:Name="gridMsg" Grid.Row="0" Margin="10,10,0,10" >
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Grid x:Name="imgGrid" Grid.Column="0">
<Image x:Name="imgMsgBoxIcon"
Source="{Binding Path=MessageIcon}"
Visibility="{Binding Path=ShowIcon}"
Width="32" Height="32"
Margin="0,0,10,0"
VerticalAlignment="Top"
MouseLeftButtonUp="ImgMsgBoxIcon_MouseLeftButtonUp"
>
</Image>
</Grid>
<Grid Grid.Column="1" VerticalAlignment="Center" >
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<TextBox Grid.Row="0" x:Name="textboxMessage"
TextWrapping="Wrap" BorderThickness="0" IsReadOnly="True"
ScrollViewer.HorizontalScrollBarVisibility="Disabled"
ScrollViewer.VerticalScrollBarVisibility="Auto"
Padding="0,0,10,0"
Background="Transparent"
Foreground="{Binding Path=MessageForeground}"
Text="{Binding Path=Message}" />
</Grid>
</Grid>
<TextBlock Grid.Row="1" x:Name="tbUrl" Margin="0,0,0,10"
Visibility="{Binding Path=ShowUrl}"
Foreground="{Binding Path=UrlForeground}"
TextWrapping="Wrap"
TextDecorations="Underline"
HorizontalAlignment="Center"
MouseLeftButtonUp="TbUrl_MouseLeftButtonUp"
Style="{StaticResource linkCursor}"
/>
<CheckBox Grid.Row="2" Margin="0,0,0,10" HorizontalAlignment="Center"
Visibility="{Binding Path=ShowCheckBox}"
Content="{Binding Path=CheckBoxData.CheckBoxText}"
IsChecked="{Binding Path=CheckBoxData.CheckBoxIsChecked}"
Foreground="{Binding Path=MessageForeground}"></CheckBox>
<Border x:Name="stackButtons" Grid.Row="3" Padding="10"
Background="{Binding Path=ButtonBackground}" >
<StackPanel Grid.Row="1" Orientation="Horizontal" HorizontalAlignment="Center" >
<Button x:Name="btnOK" Content=" OK " MinWidth="75" Margin="5,0,5,0"
Width="{Binding Path=ButtonWidth}"
Height="25"
Visibility="{Binding Path=ShowOk}"
IsDefault="{Binding Path=IsDefaultOK}"
Click="BtnOK_Click" />
<Button x:Name="btnYes" Content=" Yes " MinWidth="75" Margin="5,0,5,0"
Width="{Binding Path=ButtonWidth}"
Visibility="{Binding Path=ShowYes}"
IsDefault="{Binding Path=IsDefaultYes}"
Click="BtnYes_Click"/>
<Button x:Name="btnNo" Content=" No " MinWidth="75" Margin="5,0,5,0"
Width="{Binding Path=ButtonWidth}"
Visibility="{Binding Path=ShowNo}"
IsDefault="{Binding Path=IsDefaultNo}"
Click="BtnNo_Click"/>
<Button x:Name="btnAbort" Content=" Abort " MinWidth="75" Margin="5,0,5,0"
Width="{Binding Path=ButtonWidth}"
Visibility="{Binding Path=ShowAbort}"
IsDefault="{Binding Path=IsDefaultAbort}"
Click="BtnAbort_Click"/>
<Button x:Name="btnRetry" Content=" Retry " MinWidth="75" Margin="5,0,5,0"
Width="{Binding Path=ButtonWidth}"
Visibility="{Binding Path=ShowRetry}"
IsDefault="{Binding Path=IsDefaultRetry}"
Click="BtnRetry_Click"/>
<Button x:Name="btnIgnore" Content=" Ignore " MinWidth="75" Margin="5,0,5,0"
Width="{Binding Path=ButtonWidth}"
Visibility="{Binding Path=ShowIgnore}"
IsDefault="{Binding Path=IsDefaultIgnore}"
Click="BtnIgnore_Click"/>
<Button x:Name="btnCancel" Content=" Cancel " MinWidth="75" Margin="5,0,5,0"
Width="{Binding Path=ButtonWidth}"
Visibility="{Binding Path=ShowCancel}"
IsDefault="{Binding Path=IsDefaultCancel}"
Click="BtnCancel_Click"/>
</StackPanel>
</Border>
<Expander Grid.Row="4" Header=" Details"
IsExpanded="{Binding Path=Expanded}"
Margin="3,8,0,0"
Visibility="{Binding Path=ShowDetailsBtn}"
>
<Expander.Template>
<ControlTemplate TargetType="{x:Type Expander}">
<Grid SnapsToDevicePixels="true">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="6" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="6" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
<RowDefinition Height="6" />
</Grid.RowDefinitions>
<Grid SnapsToDevicePixels="False" Background="Transparent" Grid.Column="1" Grid.Row="0" Grid.RowSpan="2">
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<ToggleButton Grid.Column="0" MinHeight="0" MinWidth="0" Name="HeaderToggle"
Style="{StaticResource GroupBoxExpanderToggleButtonStyle}"
IsChecked="{Binding Path=IsExpanded, Mode=TwoWay, RelativeSource={RelativeSource TemplatedParent}}" />
<ContentPresenter ContentSource="Header" RecognizesAccessKey="true"
TextElement.Foreground="{Binding ElementName=textboxMessage, Path=Foreground}"
SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"
Grid.Column="1" VerticalAlignment="Center" HorizontalAlignment="Left"
/>
</Grid>
<ContentPresenter x:Name="ExpandSite" Visibility="Collapsed" Grid.Row="2" Grid.Column="1" Grid.ColumnSpan="2"
Margin="{TemplateBinding Padding}"
SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" />
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="IsExpanded" Value="true">
<Setter Property="Visibility" TargetName="ExpandSite" Value="Visible"/>
</Trigger>
<Trigger Property="IsEnabled" Value="false">
<Setter Property="Foreground" Value="{Binding Path=MessageForeground}" />
</Trigger>
<Trigger Property="IsEnabled" Value="true">
<Setter Property="Foreground" Value="{Binding Path=MessageForeground}" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Expander.Template>
<TextBox x:Name="textboxDetails" Grid.Row="2" Margin="0,10,0,0" Text="{Binding Path=DetailsText}" Height="100"
TextWrapping="Wrap" BorderThickness="0" IsReadOnly="True"
ScrollViewer.HorizontalScrollBarVisibility="Disabled"
ScrollViewer.VerticalScrollBarVisibility="Auto"
Padding="0,0,10,0"
Background="Transparent"
Foreground="{Binding Path=MessageForeground}"
/>
</Expander>
</Grid>
</Window>

View File

@@ -0,0 +1,893 @@

using System.ComponentModel;
using System.Drawing;
using System.Globalization;
using System.Media;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Interop;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using MpvNet.Help;
namespace MpvNet.Windows.WPF.MsgBox;
public partial class MessageBoxEx : Window, INotifyPropertyChanged
{
#region INotifyPropertyChanged
private bool isModified = false;
public bool IsModified {
get { return isModified; }
set {
if (value != isModified)
{
isModified = true;
NotifyPropertyChanged();
}
}
}
public event PropertyChangedEventHandler? PropertyChanged;
protected void NotifyPropertyChanged([CallerMemberName] string propertyName = "")
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
if (propertyName != "IsModified")
IsModified = true;
}
}
#endregion INotifyPropertyChanged
#region fields
private string? message;
private string? messageTitle;
private MessageBoxButton? buttons;
private MessageBoxResult messageResult;
private MessageBoxButtonEx? buttonsEx;
private MessageBoxResultEx messageResultEx;
private ImageSource? messageIcon;
private MessageBoxImage msgBoxImage;
private double buttonWidth = 0d;
private bool expanded = false;
private bool isDefaultOK;
private bool isDefaultCancel;
private bool isDefaultYes;
private bool isDefaultNo;
private bool isDefaultAbort;
private bool isDefaultRetry;
private bool isDefaultIgnore;
private bool usingExButtons = false;
#endregion fields
#region properties
public string Message {
get { return message; }
set {
if (value != message)
{
message = value;
NotifyPropertyChanged();
}
}
}
public string MessageTitle {
get { return messageTitle; }
set {
if (value != messageTitle)
{
messageTitle = value;
NotifyPropertyChanged();
}
}
}
public MessageBoxResult MessageResult { get { return this.messageResult; } set { this.messageResult = value; } }
public MessageBoxResultEx MessageResultEx { get { return this.messageResultEx; } set { this.messageResultEx = value; } }
public MessageBoxButton? Buttons {
get { return buttons; }
set {
if (value != buttons)
{
buttons = value;
NotifyPropertyChanged();
NotifyPropertyChanged("ShowOk");
NotifyPropertyChanged("ShowCancel");
NotifyPropertyChanged("ShowYes");
NotifyPropertyChanged("ShowNo");
}
}
}
public MessageBoxButtonEx? ButtonsEx {
get { return buttonsEx; }
set {
if (value != buttonsEx)
{
buttonsEx = value;
NotifyPropertyChanged();
NotifyPropertyChanged("ShowOk");
NotifyPropertyChanged("ShowCancel");
NotifyPropertyChanged("ShowYes");
NotifyPropertyChanged("ShowNo");
NotifyPropertyChanged("ShowAbort");
NotifyPropertyChanged("ShowRetry");
NotifyPropertyChanged("ShowIgnore");
}
}
}
public Visibility ShowOk => (!usingExButtons && Buttons == MessageBoxButton.OK ||
!usingExButtons && Buttons == MessageBoxButton.OKCancel ||
usingExButtons && ButtonsEx == MessageBoxButtonEx.OK ||
usingExButtons && ButtonsEx == MessageBoxButtonEx.OKCancel) ? Visibility.Visible : Visibility.Collapsed;
public Visibility ShowCancel => (!usingExButtons && Buttons == MessageBoxButton.OKCancel ||
!usingExButtons && Buttons == MessageBoxButton.YesNoCancel ||
usingExButtons && ButtonsEx == MessageBoxButtonEx.OKCancel ||
usingExButtons && ButtonsEx == MessageBoxButtonEx.YesNoCancel ||
usingExButtons && ButtonsEx == MessageBoxButtonEx.RetryCancel) ? Visibility.Visible : Visibility.Collapsed;
public Visibility ShowYes => (!usingExButtons && Buttons == MessageBoxButton.YesNo ||
!usingExButtons && Buttons == MessageBoxButton.YesNoCancel ||
usingExButtons && ButtonsEx == MessageBoxButtonEx.YesNo ||
usingExButtons && ButtonsEx == MessageBoxButtonEx.YesNoCancel) ? Visibility.Visible : Visibility.Collapsed;
public Visibility ShowNo => (!usingExButtons && Buttons == MessageBoxButton.YesNo ||
!usingExButtons && Buttons == MessageBoxButton.YesNoCancel ||
usingExButtons && ButtonsEx == MessageBoxButtonEx.YesNo ||
usingExButtons && ButtonsEx == MessageBoxButtonEx.YesNoCancel) ? Visibility.Visible : Visibility.Collapsed;
public Visibility ShowRetry => (usingExButtons && ButtonsEx == MessageBoxButtonEx.AbortRetryIgnore ||
usingExButtons && ButtonsEx == MessageBoxButtonEx.RetryCancel) ? Visibility.Visible : Visibility.Collapsed;
public Visibility ShowAbort => (usingExButtons && ButtonsEx == MessageBoxButtonEx.AbortRetryIgnore)
? Visibility.Visible
: Visibility.Collapsed;
public Visibility ShowIgnore => (usingExButtons && ButtonsEx == MessageBoxButtonEx.AbortRetryIgnore)
? Visibility.Visible
: Visibility.Collapsed;
public Visibility ShowIcon => (MessageIcon != null) ? Visibility.Visible : Visibility.Collapsed;
public ImageSource? MessageIcon {
get => messageIcon;
set {
if (value != messageIcon)
{
messageIcon = value;
NotifyPropertyChanged();
}
}
}
public double ButtonWidth {
get => buttonWidth;
set {
if (value != buttonWidth)
{
buttonWidth = value;
NotifyPropertyChanged();
}
}
}
public bool Expanded {
get => expanded;
set {
if (value != expanded)
{
expanded = value;
NotifyPropertyChanged();
}
}
}
public bool IsDefaultOK {
get => isDefaultOK;
set {
if (value != isDefaultOK)
{
isDefaultOK = value;
NotifyPropertyChanged();
}
}
}
public bool IsDefaultCancel {
get => isDefaultCancel;
set {
if (value != isDefaultCancel)
{
isDefaultCancel = value;
NotifyPropertyChanged();
}
}
}
public bool IsDefaultYes {
get => isDefaultYes;
set {
if (value != isDefaultYes)
{
isDefaultYes = value;
NotifyPropertyChanged();
}
}
}
public bool IsDefaultNo {
get => isDefaultNo;
set {
if (value != isDefaultNo)
{
isDefaultNo = value;
NotifyPropertyChanged();
}
}
}
public bool IsDefaultAbort {
get => isDefaultAbort;
set {
if (value != isDefaultAbort)
{
isDefaultAbort = value;
NotifyPropertyChanged();
}
}
}
public bool IsDefaultRetry {
get => isDefaultRetry;
set {
if (value != isDefaultRetry)
{ isDefaultRetry = value; NotifyPropertyChanged(); }
}
}
public bool IsDefaultIgnore {
get => isDefaultIgnore;
set {
if (value != isDefaultIgnore)
{
isDefaultIgnore = value;
NotifyPropertyChanged();
}
}
}
#endregion properties
#region constructors
private MessageBoxEx()
{
InitializeComponent();
DataContext = this;
LargestButtonWidth();
}
public MessageBoxEx(string msg, string title, MessageBoxButton buttons = MessageBoxButton.OK,
MessageBoxImage image = MessageBoxImage.None)
{
InitializeComponent();
DataContext = this;
Init(msg, title, buttons, image);
}
public MessageBoxEx(string msg, string title, MessageBoxButtonEx buttons = MessageBoxButtonEx.OK,
MessageBoxImage image = MessageBoxImage.None)
{
InitializeComponent();
DataContext = this;
Init(msg, title, buttons, image);
}
#endregion constructors
#region non-static methods
protected virtual void Init(string msg, string title, MessageBoxButton buttons, MessageBoxImage image)
{
InitTop(msg, title);
usingExButtons = false;
ButtonsEx = null;
Buttons = buttons;
SetButtonTemplates();
InitBottom(image);
FindDefaultButton(staticButtonDefault);
}
protected virtual void Init(string msg, string title, MessageBoxButtonEx buttons, MessageBoxImage image)
{
InitTop(msg, title);
usingExButtons = true;
Buttons = null;
ButtonsEx = buttons;
SetButtonTemplates();
InitBottom(image);
FindDefaultButtonEx(staticButtonDefault);
}
void InitTop(string msg, string title)
{
// determine whether or not to show the details pane and checkbox
ShowDetailsBtn = (string.IsNullOrEmpty(DetailsText)) ? Visibility.Collapsed : Visibility.Visible;
ShowCheckBox = (CheckBoxData == null) ? Visibility.Collapsed : Visibility.Visible;
// Well, the binding for family/size don't appear to be working, so I have to set them
// manually. Weird...
FontFamily = MsgFontFamily;
FontSize = MsgFontSize;
LargestButtonWidth();
// configure the form based on specified criteria
Message = msg;
MessageTitle = (string.IsNullOrEmpty(title.Trim())) ? "Application Message" : title;
// url (if specified)
if (Url != null)
{
tbUrl.Text = (string.IsNullOrEmpty(UrlDisplayName)) ? Url.ToString() : UrlDisplayName;
tbUrl.ToolTip = new ToolTip() { Content = Url.ToString() };
}
}
private void InitBottom(MessageBoxImage image)
{
MessageBackground = (MessageBackground == null) ? new SolidColorBrush(Colors.White) : MessageBackground;
MessageForeground = (MessageForeground == null) ? new SolidColorBrush(Colors.Black) : MessageForeground;
ButtonBackground = (ButtonBackground == null) ? new SolidColorBrush(ColorFromString("#cdcdcd")) : ButtonBackground;
MessageIcon = null;
msgBoxImage = image;
if (DelegateObj != null)
{
Style style = (Style)(FindResource("ImageOpacityChanger"));
if (style != null)
{
imgMsgBoxIcon.Style = style;
if (!string.IsNullOrEmpty(DelegateToolTip))
{
System.Windows.Controls.ToolTip tooltip = new System.Windows.Controls.ToolTip() { Content = DelegateToolTip };
// for some reason, Image elements can't do tooltips, so I assign the tootip
// to the parent grid. This seems to work fine.
imgGrid.ToolTip = tooltip;
}
}
}
// multiple images have the same ordinal value, and are indicated in the comments below.
// WTF Microsoft?
switch ((int)image)
{
case 16: // MessageBoxImage.Error, MessageBoxImage.Stop, MessageBox.Image.Hand
{
MessageIcon = GetIcon(SystemIcons.Error);
if (!isSilent)
SystemSounds.Hand.Play();
}
break;
case 64: // MessageBoxImage.Information, MessageBoxImage.Asterisk
{
MessageIcon = GetIcon(SystemIcons.Information);
if (!isSilent)
SystemSounds.Asterisk.Play();
}
break;
case 32: // MessageBoxImage.Question
{
MessageIcon = GetIcon(SystemIcons.Question);
if (!isSilent)
SystemSounds.Question.Play();
}
break;
case 48: // MessageBoxImage.Warning, MessageBoxImage.Exclamation
{
MessageIcon = GetIcon(SystemIcons.Warning);
if (!isSilent)
SystemSounds.Exclamation.Play();
}
break;
default:
MessageIcon = null;
break;
}
}
public ImageSource GetIcon(Icon icon)
{
BitmapSource image = Imaging.CreateBitmapSourceFromHIcon(
icon.Handle, Int32Rect.Empty, BitmapSizeOptions.FromEmptyOptions());
return image;
}
protected virtual void CenterInScreen()
{
double width = ActualWidth;
double height = ActualHeight;
Left = (SystemParameters.WorkArea.Width - width) / 2 + SystemParameters.WorkArea.Left;
Top = (SystemParameters.WorkArea.Height - height) / 2 + SystemParameters.WorkArea.Top;
}
protected void LargestButtonWidth()
{
Typeface typeface = new Typeface(FontFamily, FontStyle, FontWeight, FontStretch);
StackPanel panel = (StackPanel)stackButtons.Child;
double width = 0;
string largestName = string.Empty;
foreach (System.Windows.Controls.Button button in panel.Children)
{
// Using the FormattedText object
// will strip whitespace before measuring the text, so we convert spaces to double
// hyphens to compensate (I like to pad button Content with a leading and trailing
// space) so that the button is wide enough to present a more padded appearance.
FormattedText formattedText = new FormattedText(
(button.Name == "btnDetails") ? "--Details--" : ((string)(button.Content)).Replace(" ", "--"),
CultureInfo.CurrentUICulture,
System.Windows.FlowDirection.LeftToRight,
typeface,
FontSize = FontSize,
System.Windows.Media.Brushes.Black,
VisualTreeHelper.GetDpi(this).PixelsPerDip);
if (width < formattedText.Width)
largestName = button.Name;
width = Math.Max(width, formattedText.Width);
}
ButtonWidth = Math.Ceiling(width/*width + polyArrow.Width+polyArrow.Margin.Right+Margin.Left*/);
}
void SetButtonTemplates()
{
// set the button template (if specified)
if (!string.IsNullOrEmpty(ButtonTemplateName))
{
bool foundResource = true;
try
{
FindResource(ButtonTemplateName);
}
catch (Exception)
{
foundResource = false;
}
if (foundResource)
{
btnOK.SetResourceReference(Control.TemplateProperty, ButtonTemplateName);
btnYes.SetResourceReference(Control.TemplateProperty, ButtonTemplateName);
btnNo.SetResourceReference(Control.TemplateProperty, ButtonTemplateName);
btnCancel.SetResourceReference(Control.TemplateProperty, ButtonTemplateName);
btnAbort.SetResourceReference(Control.TemplateProperty, ButtonTemplateName);
btnRetry.SetResourceReference(Control.TemplateProperty, ButtonTemplateName);
btnIgnore.SetResourceReference(Control.TemplateProperty, ButtonTemplateName);
}
}
}
private void FindDefaultButtonEx(MessageBoxButtonDefault buttonDefault)
{
// determine default button
IsDefaultOK = false;
IsDefaultCancel = false;
IsDefaultYes = false;
IsDefaultNo = false;
IsDefaultAbort = false;
IsDefaultRetry = false;
IsDefaultIgnore = false;
if (buttonDefault != MessageBoxButtonDefault.None)
{
switch (ButtonsEx)
{
case MessageBoxButtonEx.OK:
IsDefaultOK = true;
break;
case MessageBoxButtonEx.OKCancel:
{
switch (buttonDefault)
{
case MessageBoxButtonDefault.Button1:
case MessageBoxButtonDefault.OK:
case MessageBoxButtonDefault.MostPositive:
IsDefaultOK = true;
break;
case MessageBoxButtonDefault.Button2:
case MessageBoxButtonDefault.Cancel:
case MessageBoxButtonDefault.LeastPositive:
IsDefaultCancel = true;
break;
case MessageBoxButtonDefault.Forms:
default:
IsDefaultOK = true;
break;
}
}
break;
case MessageBoxButtonEx.YesNoCancel:
{
switch (buttonDefault)
{
case MessageBoxButtonDefault.Button1:
case MessageBoxButtonDefault.Yes:
break;
case MessageBoxButtonDefault.MostPositive:
IsDefaultYes = true;
break;
case MessageBoxButtonDefault.Button2:
case MessageBoxButtonDefault.No:
IsDefaultNo = true;
break;
case MessageBoxButtonDefault.Button3:
case MessageBoxButtonDefault.Cancel:
case MessageBoxButtonDefault.LeastPositive:
IsDefaultCancel = true;
break;
case MessageBoxButtonDefault.Forms:
default:
IsDefaultYes = true;
break;
}
}
break;
case MessageBoxButtonEx.YesNo:
{
switch (buttonDefault)
{
case MessageBoxButtonDefault.Button1:
case MessageBoxButtonDefault.Yes:
case MessageBoxButtonDefault.MostPositive:
IsDefaultYes = true;
break;
case MessageBoxButtonDefault.Button2:
case MessageBoxButtonDefault.No:
case MessageBoxButtonDefault.LeastPositive:
IsDefaultNo = true;
break;
case MessageBoxButtonDefault.Forms:
default:
IsDefaultYes = true;
break;
}
}
break;
case MessageBoxButtonEx.RetryCancel:
{
switch (buttonDefault)
{
case MessageBoxButtonDefault.Button1:
case MessageBoxButtonDefault.Retry:
case MessageBoxButtonDefault.MostPositive:
IsDefaultRetry = true;
break;
case MessageBoxButtonDefault.Button2:
case MessageBoxButtonDefault.Cancel:
case MessageBoxButtonDefault.LeastPositive:
IsDefaultCancel = true;
break;
case MessageBoxButtonDefault.Forms:
default:
IsDefaultRetry = true;
break;
}
}
break;
case MessageBoxButtonEx.AbortRetryIgnore:
{
switch (buttonDefault)
{
case MessageBoxButtonDefault.Button1:
case MessageBoxButtonDefault.Abort:
case MessageBoxButtonDefault.LeastPositive:
IsDefaultAbort = true;
break;
case MessageBoxButtonDefault.Button2:
case MessageBoxButtonDefault.Retry:
IsDefaultRetry = true;
break;
case MessageBoxButtonDefault.Button3:
case MessageBoxButtonDefault.Ignore:
case MessageBoxButtonDefault.MostPositive:
IsDefaultIgnore = true;
break;
case MessageBoxButtonDefault.Forms:
default:
IsDefaultAbort = true;
break;
}
}
break;
}
}
}
private void FindDefaultButton(MessageBoxButtonDefault buttonDefault)
{
// determine default button
IsDefaultOK = false;
IsDefaultCancel = false;
IsDefaultYes = false;
IsDefaultNo = false;
IsDefaultAbort = false;
IsDefaultRetry = false;
IsDefaultIgnore = false;
if (buttonDefault != MessageBoxButtonDefault.None)
{
switch (Buttons)
{
case MessageBoxButton.OK:
IsDefaultOK = true;
break;
case MessageBoxButton.OKCancel:
{
switch (buttonDefault)
{
case MessageBoxButtonDefault.Button1:
case MessageBoxButtonDefault.OK:
case MessageBoxButtonDefault.MostPositive:
IsDefaultOK = true;
break;
case MessageBoxButtonDefault.Button2:
case MessageBoxButtonDefault.Cancel:
case MessageBoxButtonDefault.LeastPositive:
IsDefaultCancel = true;
break;
case MessageBoxButtonDefault.Forms:
default:
IsDefaultOK = true;
break;
}
}
break;
case MessageBoxButton.YesNoCancel:
{
switch (buttonDefault)
{
case MessageBoxButtonDefault.Button1:
case MessageBoxButtonDefault.Yes:
break;
case MessageBoxButtonDefault.MostPositive:
IsDefaultYes = true;
break;
case MessageBoxButtonDefault.Button2:
case MessageBoxButtonDefault.No:
IsDefaultNo = true;
break;
case MessageBoxButtonDefault.Button3:
case MessageBoxButtonDefault.Cancel:
case MessageBoxButtonDefault.LeastPositive:
IsDefaultCancel = true;
break;
case MessageBoxButtonDefault.Forms:
default:
IsDefaultYes = true;
break;
}
}
break;
case MessageBoxButton.YesNo:
{
switch (buttonDefault)
{
case MessageBoxButtonDefault.Button1:
case MessageBoxButtonDefault.Yes:
case MessageBoxButtonDefault.MostPositive:
IsDefaultYes = true;
break;
case MessageBoxButtonDefault.Button2:
case MessageBoxButtonDefault.No:
case MessageBoxButtonDefault.LeastPositive:
IsDefaultNo = true;
break;
case MessageBoxButtonDefault.Forms:
default:
IsDefaultYes = true;
break;
}
}
break;
}
}
}
#endregion non-static methods
#region event handlers
#region buttons
/// <summary>
/// Handle the click event for the OK button
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void BtnOK_Click(object sender, RoutedEventArgs e)
{
this.MessageResult = MessageBoxResult.OK;
this.MessageResultEx = MessageBoxResultEx.OK;
this.DialogResult = true;
}
/// <summary>
/// Handle the click event for the Yes button
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void BtnYes_Click(object sender, RoutedEventArgs e)
{
this.MessageResult = MessageBoxResult.Yes;
this.MessageResultEx = MessageBoxResultEx.Yes;
this.DialogResult = true;
}
/// <summary>
/// Handle the click event for the No button
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void BtnNo_Click(object sender, RoutedEventArgs e)
{
this.MessageResult = MessageBoxResult.No;
this.MessageResultEx = MessageBoxResultEx.No;
this.DialogResult = true;
}
private void BtnAbort_Click(object sender, RoutedEventArgs e)
{
this.MessageResult = MessageBoxResult.None;
this.MessageResultEx = MessageBoxResultEx.Abort;
this.DialogResult = true;
}
private void BtnRetry_Click(object sender, RoutedEventArgs e)
{
this.MessageResult = MessageBoxResult.None;
this.MessageResultEx = MessageBoxResultEx.Retry;
this.DialogResult = true;
}
private void BtnIgnore_Click(object sender, RoutedEventArgs e)
{
this.MessageResult = MessageBoxResult.None;
this.MessageResultEx = MessageBoxResultEx.Ignore;
this.DialogResult = true;
}
/// <summary>
/// Handle the click event for the Cancel button
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void BtnCancel_Click(object sender, RoutedEventArgs e)
{
this.MessageResult = MessageBoxResult.Cancel;
this.MessageResultEx = MessageBoxResultEx.Cancel;
this.DialogResult = true;
}
#endregion buttons
void NotifiableWindow_SizeChanged(object sender, SizeChangedEventArgs e) => CenterInScreen();
void Window_Loaded(object sender, RoutedEventArgs e)
{
imgMsgBoxIcon.ToolTip = (msgBoxImage == MessageBoxImage.Error) ? MsgBoxIconToolTip : null;
}
void Window_Closing(object sender, CancelEventArgs e)
{
DetailsText = null;
CheckBoxData = null;
staticButtonDefault = MessageBoxButtonDefault.Forms;
if (MessageResult == MessageBoxResult.None)
{
if (usingExButtons)
{
switch (ButtonsEx)
{
case MessageBoxButtonEx.OK:
MessageResultEx = MessageBoxResultEx.OK;
break;
case MessageBoxButtonEx.YesNoCancel:
case MessageBoxButtonEx.OKCancel:
case MessageBoxButtonEx.RetryCancel:
case MessageBoxButtonEx.AbortRetryIgnore:
MessageResultEx = MessageBoxResultEx.Cancel;
break;
case MessageBoxButtonEx.YesNo:
MessageResultEx = MessageBoxResultEx.No;
break;
}
}
else
{
switch (Buttons)
{
case MessageBoxButton.OK:
MessageResult = MessageBoxResult.OK;
break;
case MessageBoxButton.YesNoCancel:
case MessageBoxButton.OKCancel:
MessageResult = MessageBoxResult.Cancel;
break;
case MessageBoxButton.YesNo:
MessageResult = MessageBoxResult.No;
break;
}
}
}
}
void ImgMsgBoxIcon_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
{
if (DelegateObj != null && msgBoxImage == MessageBoxImage.Error && Buttons == MessageBoxButton.OK)
{
DelegateObj.PerformAction(Message);
if (ExitAfterErrorAction)
{
MessageResult = MessageBoxResult.None;
DialogResult = true;
}
}
}
void TbUrl_MouseLeftButtonUp(object sender, MouseButtonEventArgs e) => ProcessHelp.ShellExecute(Url?.ToString());
[DllImport("user32.dll")]
private static extern IntPtr GetSystemMenu(IntPtr hWnd, bool bRevert);
[DllImport("user32.dll")]
private static extern bool EnableMenuItem(IntPtr hMenu, uint uIDEnableItem, uint uEnable);
private const uint MF_BYCOMMAND = 0x00000000;
private const uint MF_GRAYED = 0x00000001;
private const uint SC_CLOSE = 0xF060;
void Window_SourceInitialized(object sender, EventArgs e)
{
if (!enableCloseButton)
{
var hWnd = new WindowInteropHelper(this);
var sysMenu = GetSystemMenu(hWnd.Handle, false);
EnableMenuItem(sysMenu, SC_CLOSE, MF_BYCOMMAND | MF_GRAYED);
}
}
#endregion event handlers
}

View File

@@ -0,0 +1,56 @@

using System.ComponentModel;
using System.Runtime.CompilerServices;
namespace MpvNet.Windows.WPF.MsgBox;
public class MsgBoxExCheckBoxData : INotifyPropertyChanged
{
private bool isModified = false;
public bool IsModified {
get => isModified;
set {
if (value != isModified)
{
isModified = true;
NotifyPropertyChanged();
}
}
}
public event PropertyChangedEventHandler? PropertyChanged;
protected void NotifyPropertyChanged([CallerMemberName] string propertyName = "")
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
if (propertyName != "IsModified")
IsModified = true;
}
}
private string? checkBoxText;
private bool checkBoxIsChecked;
public string? CheckBoxText {
get => checkBoxText;
set {
if (value != checkBoxText)
checkBoxText = value; NotifyPropertyChanged();
}
}
public bool CheckBoxIsChecked {
get => checkBoxIsChecked;
set {
if (value != checkBoxIsChecked)
{
checkBoxIsChecked = value;
NotifyPropertyChanged();
}
}
}
}

View File

@@ -0,0 +1,16 @@

using System.Windows;
namespace MpvNet.Windows.WPF.MsgBox;
public abstract class MsgBoxExDelegate
{
public string? Message { get; set; }
public string? Details { get; set; }
public DateTime MessageDate { get; set; }
public virtual MessageBoxResult PerformAction(string message, string? details = null)
{
throw new NotImplementedException();
}
}

View File

@@ -0,0 +1,215 @@

// https://www.codeproject.com/Articles/5290638/Customizable-WPF-MessageBox
using System.ComponentModel;
using System.Drawing.Text;
using System.Runtime.InteropServices;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Interop;
using System.Windows.Media;
namespace MpvNet.Windows.WPF.MsgBox;
public partial class MessageBoxEx : Window, INotifyPropertyChanged
{
#region fields
private static double screenWidth = SystemParameters.WorkArea.Width - 100;
private static bool enableCloseButton = true;
private static bool isSilent = false;
private static List<string> installedFonts = new List<string>();
public static MessageBoxButtonDefault staticButtonDefault;
#endregion fields
#region properties
public static Color DefaultUrlForegroundColor => Colors.Blue;
private static string? MsgBoxIconToolTip { get; set; }
protected static MsgBoxExDelegate? DelegateObj { get; set; }
protected static bool ExitAfterErrorAction { get; set; }
public static ContentControl? ParentWindow { get; set; }
public static string? ButtonTemplateName { get; set; }
public static Brush? MessageBackground { get; set; }
public static Brush? MessageForeground { get; set; }
public static Brush? ButtonBackground { get; set; }
public static double MaxFormWidth { get; set; } = screenWidth;
public static Visibility ShowDetailsBtn { get; set; } = Visibility.Collapsed;
public static string? DetailsText { get; set; }
public static Visibility ShowCheckBox { get; set; } = Visibility.Collapsed;
public static MsgBoxExCheckBoxData? CheckBoxData { get; set; } = null;
public static FontFamily MsgFontFamily { get; set; } = new FontFamily("Segoe UI");
public static double MsgFontSize { get; set; } = 12;
public static Uri? Url { get; set; } = null;
public static Visibility ShowUrl { get; set; } = Visibility.Collapsed;
public static string? UrlDisplayName { get; set; } = null;
public static SolidColorBrush UrlForeground { get; set; } = new SolidColorBrush(DefaultUrlForegroundColor);
public static string? DelegateToolTip { get; set; }
#endregion properties
#region methods
public static void SetFont(string familyName) => MsgFontFamily = new FontFamily(familyName);
public static MessageBoxResult OpenMessageBox(
string msg, string title, MessageBoxButton buttons, MessageBoxImage image)
{
MessageBoxEx window = new MessageBoxEx(msg, title, buttons, image);
SetOwner(window);
window.ShowDialog();
return window.MessageResult;
}
public static MessageBoxResultEx OpenMessageBox(
string msg,
string title,
MessageBoxButtonEx buttons,
MessageBoxImage image)
{
MessageBoxEx window = new MessageBoxEx(msg, title, buttons, image);
SetOwner(window);
window.ShowDialog();
return window.MessageResultEx;
}
public static void SetOwner(Window window)
{
IntPtr ownerHandle = GetOwnerHandle();
if (ownerHandle != IntPtr.Zero)
new WindowInteropHelper(window).Owner = ownerHandle;
}
public static IntPtr GetOwnerHandle()
{
IntPtr foregroundWindow = GetForegroundWindow();
GetWindowThreadProcessId(foregroundWindow, out var procID);
using (var proc = Process.GetCurrentProcess())
if (proc.Id == procID)
return foregroundWindow;
return IntPtr.Zero;
}
[DllImport("user32.dll")]
public static extern IntPtr GetForegroundWindow();
[DllImport("user32.dll")]
public static extern uint GetWindowThreadProcessId(IntPtr hWnd, out uint lpdwProcessId);
public static Color ColorFromString(string colorString)
{
Color wpfColor = Colors.Black;
try {
wpfColor = (Color)ColorConverter.ConvertFromString(colorString);
} catch (Exception) { }
return wpfColor;
}
public static void SetFont()
{
MsgFontFamily = Application.Current.MainWindow.FontFamily;
MsgFontSize = Application.Current.MainWindow.FontSize;
}
public static void SetFont(ContentControl parent)
{
MsgFontFamily = parent.FontFamily;
MsgFontSize = parent.FontSize;
}
public static void SetFont(string familyName, double size)
{
if (!IsFontFamilyValid(familyName))
if (!string.IsNullOrEmpty(familyName))
MsgFontFamily = new FontFamily(familyName);
MsgFontSize = Math.Max(1.0, size);
}
private static bool IsFontFamilyValid(string name)
{
if (installedFonts.Count == 0)
using (InstalledFontCollection fontsCollection = new InstalledFontCollection())
installedFonts = (from x in fontsCollection.Families select x.Name).ToList();
return installedFonts.Contains(name);
}
public static void SetButtonTemplateName(string name)
{
ButtonTemplateName = name;
}
public static void SetMaxFormWidth(double value)
{
MaxFormWidth = Math.Max(value, 300);
double minWidth = 300;
MaxFormWidth = Math.Max(minWidth, Math.Min(value, screenWidth));
}
public static void ResetToDefaults()
{
MsgFontSize = 12d;
MsgFontFamily = new FontFamily("Segoe UI");
DelegateObj = null;
DetailsText = null;
MessageForeground = null;
MessageBackground = null;
ButtonBackground = null;
ParentWindow = null;
isSilent = false;
enableCloseButton = true;
ButtonTemplateName = null;
MsgBoxIconToolTip = null;
ShowCheckBox = Visibility.Collapsed;
CheckBoxData = null;
ExitAfterErrorAction = false;
MaxFormWidth = 800;
Url = null;
ShowUrl = Visibility.Collapsed;
UrlDisplayName = null;
UrlForeground = new SolidColorBrush(DefaultUrlForegroundColor);
staticButtonDefault = MessageBoxButtonDefault.Forms;
}
public static void EnableCloseButton(bool enable)
{
enableCloseButton = enable;
}
public static void SetAsSilent(bool quiet)
{
isSilent = quiet;
}
public static void SetDefaultButton(MessageBoxButtonDefault buttonDefault)
{
staticButtonDefault = buttonDefault;
}
#endregion methods
}

View File

@@ -0,0 +1,24 @@

namespace MpvNet.Windows.WPF.MsgBox;
public class MsgBoxExtendedFunctionality
{
public MessageBoxButtonDefault ButtonDefault { get; set; }
public string? DetailsText { get; set; }
public MsgBoxExCheckBoxData? CheckBoxData { get; set; }
public MsgBoxExDelegate? MessageDelegate { get; set; }
public bool ExitAfterAction { get; set; }
public string DelegateToolTip { get; set; }
public MsgBoxUrl? URL { get; set; }
public MsgBoxExtendedFunctionality()
{
ButtonDefault = MessageBoxButtonDefault.Forms;
DetailsText = null;
CheckBoxData = null;
MessageDelegate = null;
URL = null;
DelegateToolTip = "Click this icon for additional info/actions.";
}
}

View File

@@ -0,0 +1,13 @@

using System.Windows.Media;
namespace MpvNet.Windows.WPF.MsgBox;
public class MsgBoxUrl
{
public Uri? URL { get; set; }
public string? DisplayName { get; set; }
public Color Foreground { get; set; }
public MsgBoxUrl() => Foreground = MessageBoxEx.DefaultUrlForegroundColor;
}

View File

@@ -0,0 +1,15 @@

namespace MpvNet.Windows.WPF.MsgBox;
public enum MessageBoxButtonEx { OK = 0, OKCancel, AbortRetryIgnore, YesNoCancel, YesNo, RetryCancel }
public enum MessageBoxResultEx { None = 0, OK, Cancel, Abort, Retry, Ignore, Yes, No }
public enum MessageBoxButtonDefault
{
OK, Cancel, Yes, No, Abort, Retry, Ignore, // specific button
Button1, Button2, Button3, // button by ordinal left-to-right position
MostPositive, LeastPositive, // button by positivity
Forms, // button according to the Windows.Forms standard messagebox
None // no default button
}