This commit is contained in:
2026-04-17 14:55:32 -04:00
commit bc3ac1d4c9
18017 changed files with 4371742 additions and 0 deletions

View File

@@ -0,0 +1,371 @@
using System;
using System.Text;
using System.Runtime.InteropServices;
using System.Windows;
using System.Windows.Interop;
namespace DTS.Common.Dialogs
{
/// <summary>
/// Represents a common dialog box (Win32::SHBrowseForFolder()) that allows a user to select a folder.
/// </summary>
public class BrowseForFolderDialog
{
#region Public Properties
/// <summary>
/// Gets the current and or final selected folder path.
/// </summary>
public string SelectedFolder { get; protected set; }
/// <summary>
/// Gets or sets the string that is displayed above the tree View control in the dialog box (must set BEFORE calling ShowDialog()).
/// </summary>
public string Title
{
get => BrowseInfo.lpszTitle;
set => BrowseInfo.lpszTitle = value;
}
/// <summary>
/// Gets or sets the initially selected folder path.
/// </summary>
public string InitialFolder { get; set; }
/// <summary>
/// Gets or sets the initially selected and expanded folder path. Overrides SelectedFolder.
/// </summary>
public string InitialExpandedFolder { get; set; }
/// <summary>
/// Gets or sets the text for the dialog's OK button.
/// </summary>
public string OKButtonText { get; set; }
/// <summary>
/// Provides direct access to the Win32::SHBrowseForFolder() BROWSEINFO structure used to create the dialog in ShowDialog().
/// </summary>
public BROWSEINFOW BrowseInfo { get; protected set; }
/// <summary>
/// Provides direct access to the ulFlags field of the Win32::SHBrowseForFolder() structure used to create the dialog in ShowDialog().
/// </summary>
public BrowseInfoFlags BrowserDialogFlags
{
get => BrowseInfo.ulFlags;
set => BrowseInfo.ulFlags = value;
}
#endregion
#region Public Constructors
/// <summary>
/// Constructs a BrowseForFolderDialog with default BrowseInfoFlags set to BIF_NEWDIALOGSTYLE.
/// </summary>
public BrowseForFolderDialog()
{
BrowseInfo = new BROWSEINFOW();
BrowseInfo.hwndOwner = IntPtr.Zero;
BrowseInfo.pidlRoot = IntPtr.Zero;
BrowseInfo.pszDisplayName = new String(' ', 260);
BrowseInfo.lpszTitle = "Select a folder:";
BrowseInfo.ulFlags = BrowseInfoFlags.BIF_NEWDIALOGSTYLE;
BrowseInfo.lpfn = new BrowseCallbackProc(BrowseEventHandler);
BrowseInfo.lParam = IntPtr.Zero;
BrowseInfo.iImage = -1;
}
#endregion
#region Public ShowDialog() Overloads
/// <summary>
/// Shows the dialog (Win32::SHBrowseForFolder()).
/// </summary>
public Nullable<bool> ShowDialog()
{
return PInvokeSHBrowseForFolder(null);
}
/// <summary>
/// Shows the dialog (Win32::SHBrowseForFolder()) with its hwndOwner set to the handle of 'owner'.
/// </summary>
public Nullable<bool> ShowDialog(Window owner)
{
return PInvokeSHBrowseForFolder(owner);
}
#endregion
#region PInvoke Stuff
private Nullable<bool> PInvokeSHBrowseForFolder(Window owner)
{
WindowInteropHelper windowhelper;
if (null != owner)
{
windowhelper = new WindowInteropHelper(owner);
BrowseInfo.hwndOwner = windowhelper.Handle;
}
var pidl = SHBrowseForFolderW(BrowseInfo);
if (IntPtr.Zero != pidl)
{
var pathsb = new StringBuilder(260);
if (SHGetPathFromIDList(pidl, pathsb))
{
SelectedFolder = pathsb.ToString();
Marshal.FreeCoTaskMem(pidl);
return true;
}
}
return false;
}
private int BrowseEventHandler(IntPtr hwnd, MessageFromBrowser uMsg, IntPtr lParam, IntPtr lpData)
{
switch (uMsg)
{
case MessageFromBrowser.BFFM_INITIALIZED:
{
// The dialog box has finished initializing.
// lParam Not used, value is NULL.
if (!string.IsNullOrEmpty(InitialExpandedFolder))
SendMessageW(hwnd, MessageToBrowser.BFFM_SETEXPANDED, new IntPtr(1), InitialExpandedFolder);
else if (!string.IsNullOrEmpty(InitialFolder))
SendMessageW(hwnd, MessageToBrowser.BFFM_SETSELECTIONW, new IntPtr(1), InitialFolder);
if (!string.IsNullOrEmpty(OKButtonText))
SendMessageW(hwnd, MessageToBrowser.BFFM_SETOKTEXT, new IntPtr(1), OKButtonText);
break;
}
case MessageFromBrowser.BFFM_SELCHANGED:
{
// The selection has changed in the dialog box.
// lParam A pointer to an item identifier list (PIDL) identifying the newly selected item.
var pathsb = new StringBuilder(260);
if (SHGetPathFromIDList(lParam, pathsb))
{
SelectedFolder = pathsb.ToString();
}
break;
}
case MessageFromBrowser.BFFM_VALIDATEFAILEDA: // ANSI
{
// The user typed an invalid name into the dialog's edit box. A nonexistent folder is considered an invalid name.
// lParam A pointer to a string containing the invalid name. An application can use this data in an error dialog informing the user that the name was not valid.
// Return zero to dismiss the dialog or nonzero to keep the dialog displayed
break;
}
case MessageFromBrowser.BFFM_VALIDATEFAILEDW: // Unicode
{
// The user typed an invalid name into the dialog's edit box. A nonexistent folder is considered an invalid name.
// lParam A pointer to a string containing the invalid name. An application can use this data in an error dialog informing the user that the name was not valid.
// Return zero to dismiss the dialog or nonzero to keep the dialog displayed
break;
}
case MessageFromBrowser.BFFM_IUNKNOWN:
{
// An IUnknown interface is available to the dialog box.
// lParam A pointer to an IUnknown interface.
break;
}
}
return 0;
}
public delegate int BrowseCallbackProc(IntPtr hwnd, MessageFromBrowser uMsg, IntPtr lParam, IntPtr lpData);
[Flags]
public enum BrowseInfoFlags : uint
{
/// <summary>
/// No specified BIF_xxx flags.
/// </summary>
BIF_None = 0x0000,
/// <summary>
/// Only return file system directories. If the user selects folders that are not part of the file system, the OK button is grayed.
/// </summary>
BIF_RETURNONLYFSDIRS = 0x0001, // For finding a folder to start document searching
/// <summary>
/// Do not include network folders below the domain level in the dialog box's tree View control.
/// </summary>
BIF_DONTGOBELOWDOMAIN = 0x0002, // For starting the Find Computer
/// <summary>
/// Include a status area in the dialog box.
/// </summary>
BIF_STATUSTEXT = 0x0004, // Top of the dialog has 2 lines of text for BROWSEINFO.lpszTitle and one line if
// this flag is set. Passing the message BFFM_SETSTATUSTEXTA to the hwnd can set the
// rest of the text. This is not used with BIF_USENEWUI and BROWSEINFO.lpszTitle gets
// all three lines of text.
/// <summary>
/// Only return file system ancestors. An ancestor is a subfolder that is beneath the root folder in the namespace hierarchy.
/// </summary>
BIF_RETURNFSANCESTORS = 0x0008,
/// <summary>
/// Include an edit control in the browse dialog box that allows the user to type the name of an item.
/// </summary>
BIF_EDITBOX = 0x0010, // Add an editbox to the dialog
/// <summary>
/// If the user types an invalid name into the edit box, the browse dialog box will call the application's BrowseCallbackProc with the BFFM_VALIDATEFAILED message.
/// </summary>
BIF_VALIDATE = 0x0020, // insist on valid result (or CANCEL)
/// <summary>
/// Use the new user interface. Setting this flag provides the user with a larger dialog box that can be resized.
/// </summary>
BIF_NEWDIALOGSTYLE = 0x0040, // Use the new dialog layout with the ability to resize
// Caller needs to call OleInitialize() before using this API
/// <summary>
/// Use the new user interface, including an edit box. This flag is equivalent to BIF_EDITBOX | BIF_NEWDIALOGSTYLE.
/// </summary>
BIF_USENEWUI = BIF_NEWDIALOGSTYLE | BIF_EDITBOX,
/// <summary>
/// The browse dialog box can display URLs. The BIF_USENEWUI and BIF_BROWSEINCLUDEFILES flags must also be set.
/// </summary>
BIF_BROWSEINCLUDEURLS = 0x0080, // Allow URLs to be displayed or entered. (Requires BIF_USENEWUI)
/// <summary>
/// When combined with BIF_NEWDIALOGSTYLE, adds a usage hint to the dialog box in place of the edit box.
/// </summary>
BIF_UAHINT = 0x0100, // Add a UA hint to the dialog, in place of the edit box. May not be combined with BIF_EDITBOX
/// <summary>
/// Do not include the New Folder button in the browse dialog box.
/// </summary>
BIF_NONEWFOLDERBUTTON = 0x0200, // Do not add the "New Folder" button to the dialog. Only applicable with BIF_NEWDIALOGSTYLE.
/// <summary>
/// When the selected item is a shortcut, return the PIDL of the shortcut itself rather than its target.
/// </summary>
BIF_NOTRANSLATETARGETS = 0x0400,// don't traverse target as shortcut
/// <summary>
/// Only return computers. If the user selects anything other than a computer, the OK button is grayed.
/// </summary>
BIF_BROWSEFORCOMPUTER = 0x1000, // Browsing for Computers.
/// <summary>
/// Only allow the selection of printers. If the user selects anything other than a printer, the OK button is grayed.
/// </summary>
BIF_BROWSEFORPRINTER = 0x2000, // Browsing for Printers
/// <summary>
/// The browse dialog box will display files as well as folders.
/// </summary>
BIF_BROWSEINCLUDEFILES = 0x4000,// Browsing for Everything
/// <summary>
/// The browse dialog box can display shareable resources on remote systems.
/// </summary>
BIF_SHAREABLE = 0x8000 // sharable resources displayed (remote shares, requires BIF_USENEWUI)
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
public class BROWSEINFOW
{
/// <summary>
/// A handle to the owner window for the dialog box.
/// </summary>
public IntPtr hwndOwner;
/// <summary>
/// A pointer to an item identifier list (PIDL) specifying the location of the root folder from which to start browsing.
/// </summary>
public IntPtr pidlRoot; // PCIDLIST_ABSOLUTE
/// <summary>
/// The address of a buffer to receive the display name of the folder selected by the user. The size of this buffer is assumed to be MAX_PATH characters.
/// </summary>
public string pszDisplayName; // Output parameter! (length must be >= MAX_PATH)
/// <summary>
/// The address of a null-terminated string that is displayed above the tree View control in the dialog box.
/// </summary>
public string lpszTitle;
/// <summary>
/// Flags specifying the options for the dialog box.
/// </summary>
public BrowseInfoFlags ulFlags;
/// <summary>
/// A BrowseCallbackProc delegate that the dialog box calls when an event occurs.
/// </summary>
public BrowseCallbackProc lpfn;
/// <summary>
/// An application-defined value that the dialog box passes to the BrowseCallbackProc delegate, if one is specified.
/// </summary>
public IntPtr lParam;
/// <summary>
/// A variable to receive the image associated with the selected folder. The image is specified as an index to the system image list.
/// </summary>
public int iImage; // Output parameter!
}
// message from browser
public enum MessageFromBrowser : uint
{
/// <summary>
/// The dialog box has finished initializing.
/// </summary>
BFFM_INITIALIZED = 1,
/// <summary>
/// The selection has changed in the dialog box.
/// </summary>
BFFM_SELCHANGED = 2,
/// <summary>
/// (ANSI) The user typed an invalid name into the dialog's edit box. A nonexistent folder is considered an invalid name.
/// </summary>
BFFM_VALIDATEFAILEDA = 3,
/// <summary>
/// (Unicode) The user typed an invalid name into the dialog's edit box. A nonexistent folder is considered an invalid name.
/// </summary>
BFFM_VALIDATEFAILEDW = 4,
/// <summary>
/// An IUnknown interface is available to the dialog box.
/// </summary>
BFFM_IUNKNOWN = 5
}
// messages to browser
public enum MessageToBrowser : uint
{
/// <summary>
/// Win32 API macro - start of user defined window message range.
/// </summary>
WM_USER = 0x0400,
/// <summary>
/// (ANSI) Sets the status text. Set lpData to point to a null-terminated string with the desired text.
/// </summary>
BFFM_SETSTATUSTEXTA = WM_USER + 100,
/// <summary>
/// Enables or disables the dialog box's OK button. lParam - To enable, set to a nonzero value. To disable, set to zero.
/// </summary>
BFFM_ENABLEOK = WM_USER + 101,
/// <summary>
/// (ANSI) Specifies the path of a folder to select.
/// </summary>
BFFM_SETSELECTIONA = WM_USER + 102,
/// <summary>
/// (Unicode) Specifies the path of a folder to select.
/// </summary>
BFFM_SETSELECTIONW = WM_USER + 103,
/// <summary>
/// (Unicode) Sets the status text. Set lpData to point to a null-terminated string with the desired text.
/// </summary>
BFFM_SETSTATUSTEXTW = WM_USER + 104,
/// <summary>
/// Sets the text that is displayed on the dialog box's OK button.
/// </summary>
BFFM_SETOKTEXT = WM_USER + 105, // Unicode only
/// <summary>
/// Specifies the path of a folder to expand in the Browse dialog box.
/// </summary>
BFFM_SETEXPANDED = WM_USER + 106 // Unicode only
}
[DllImport("shell32.dll")]
private static extern IntPtr SHBrowseForFolderW([MarshalAs(UnmanagedType.LPStruct), In, Out] BROWSEINFOW bi);
[DllImport("shell32.dll")]
private static extern bool SHGetPathFromIDList(IntPtr pidl, StringBuilder path);
[DllImport("user32.dll")]
public static extern IntPtr SendMessageW(IntPtr hWnd, uint Msg, IntPtr wParam, IntPtr lParam);
[DllImport("user32.dll")]
public static extern IntPtr SendMessageW(IntPtr hWnd, MessageToBrowser msg, IntPtr wParam, [MarshalAs(UnmanagedType.LPWStr)] string str);
#endregion
}
}

View File

@@ -0,0 +1,44 @@
using System.Windows;
using Microsoft.Practices.Prism.Interactivity.InteractionRequest;
namespace DTS.Common.Dialogs
{
/// <summary>
/// Represents an interaction request used for confirmations.
/// </summary>
public class ConfirmationEx : Confirmation
{
/// <summary>
/// Gets or sets the type of buttons displayed in the confirmation window.
/// </summary>
public MessageBoxButton Buttons { get; set; } = MessageBoxButton.OKCancel;
/// <summary>
/// Gets or sets the type of image displayed in the confirmation window.
/// </summary>
public MessageBoxImage Image { get; set; } = MessageBoxImage.Question;
/// <summary>
/// Returns result of the confirmation window.
/// </summary>
public MessageBoxResult Result { get; set; }
///// <summary>
///// The number of milliseconds before dialog auto-closes (0 = remain open).
///// </summary>
//public int Timeout
//{
// get { return _timeout; }
// set { _timeout = value; }
//}
///// <summary>
///// Returns result of the confirmation window.
///// </summary>
//public MessageBoxResult TimeoutResult
//{
// get { return _timeoutResult; }
// set { _timeoutResult = value; }
//}
}
}

View File

@@ -0,0 +1,64 @@
<Window x:Class="DTS.Common.Dialogs.ConfirmationWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
xmlns:ei="http://schemas.microsoft.com/expression/2010/interactions"
Title="{Binding Title}" Icon="..\Images\question_48.png" WindowStyle="ToolWindow" ResizeMode="NoResize"
SizeToContent="WidthAndHeight" MinWidth="300" MaxWidth="500" MinHeight="150" MaxHeight="500"
x:Name="confirmationWindow">
<Window.Resources>
<Style x:Key="ReadOnlyTextBox" TargetType="TextBox">
<Setter Property="IsReadOnly" Value="True" />
<Setter Property="TextWrapping" Value="Wrap" />
<Setter Property="Height" Value="Auto"/>
<Setter Property="Width" Value="Auto"/>
<Setter Property="Margin" Value="5"/>
<Setter Property="Background" Value="White"/>
<Setter Property="BorderBrush" Value="White"/>
<Setter Property="BorderThickness" Value="0"/>
<Setter Property="IsTabStop" Value="False"/>
<Setter Property="TextAlignment" Value="Left"/>
<Setter Property="HorizontalAlignment" Value="Left"/>
<Setter Property="VerticalAlignment" Value="Center"/>
<Setter Property="HorizontalScrollBarVisibility" Value="Hidden"/>
<Setter Property="VerticalScrollBarVisibility" Value="Auto"/>
</Style>
</Window.Resources>
<Grid x:Name="LayoutRoot" Margin="2">
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="48"/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<Image Grid.Row="0" Grid.Column="0" Source="..\Images\question_48.png" VerticalAlignment="Center" HorizontalAlignment="Center" Margin="5,5,0,5"/>
<ContentControl Grid.Row="0" Grid.Column="1" ContentTemplate="{Binding ConfirmationTemplate, ElementName=confirmationWindow}"
HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Margin="0,10,10,5">
<TextBox Text="{Binding Content}" Style="{StaticResource ReadOnlyTextBox}"/>
</ContentControl>
<StackPanel Grid.Row="1" Grid.ColumnSpan="2" Grid.Column="0" Orientation="Horizontal" HorizontalAlignment="Right" Margin="5,5,10,5">
<Button x:Name="OKButton" Content="OK" Width="70" Height="25" Cursor="Hand" HorizontalAlignment="Right" Margin="5,0">
<i:Interaction.Triggers>
<i:EventTrigger EventName="Click">
<ei:ChangePropertyAction PropertyName="Confirmed" TargetObject="{Binding}" Value="True"/>
<ei:CallMethodAction TargetObject="{Binding ElementName=confirmationWindow}" MethodName="Close"/>
</i:EventTrigger>
</i:Interaction.Triggers>
</Button>
<Button x:Name="CancelButton" Content="Cancel" Width="70" Height="25" Cursor="Hand" HorizontalAlignment="Right">
<i:Interaction.Triggers>
<i:EventTrigger EventName="Click">
<ei:CallMethodAction TargetObject="{Binding ElementName=confirmationWindow}" MethodName="Close"/>
</i:EventTrigger>
</i:Interaction.Triggers>
</Button>
</StackPanel>
</Grid>
</Window>

View File

@@ -0,0 +1,65 @@
using System;
using System.Windows;
using System.Runtime.InteropServices;
using System.Windows.Interop;
namespace DTS.Common.Dialogs
{
/// <summary>
/// Interaction logic for ConfirmationWindow.xaml
/// </summary>
public partial class ConfirmationWindow
{
/// <summary>
/// The content template to use when showing <see cref="Microsoft.Practices.Prism.Interactivity.InteractionRequest.Confirmation"/> data.
/// </summary>
public static readonly DependencyProperty ConfirmationTemplateProperty =
DependencyProperty.Register(
"ConfirmationTemplate",
typeof (DataTemplate),
typeof (ConfirmationWindow),
new PropertyMetadata(null));
/// <summary>
/// Creates a new instance of ConfirmationChildWindow.
/// </summary>
public ConfirmationWindow()
{
// Hook the SourceInitialized event and then using the SetWindowLong function to strip off the WS_SYSMENU bit.
SourceInitialized += ConfirmationWindow_SourceInitialized;
InitializeComponent();
}
/// <summary>
/// The content template to use when showing <see cref="Microsoft.Practices.Prism.Interactivity.InteractionRequest.Confirmation"/> data.
/// </summary>
public DataTemplate ConfirmationTemplate
{
get => (DataTemplate) GetValue(ConfirmationTemplateProperty);
set => SetValue(ConfirmationTemplateProperty, value);
}
private void ConfirmationWindow_SourceInitialized(object sender, EventArgs e)
{
// Removing the Close Button on a WPF window.
// The key here is hooking the SourceInitialized event and then using the SetWindowLong function to strip off the WS_SYSMENU bit.
var wih = new WindowInteropHelper(this);
var style = GetWindowLong(wih.Handle, GWL_STYLE);
SetWindowLong(wih.Handle, GWL_STYLE, style & ~WS_SYSMENU);
}
private const int GWL_STYLE = -16;
private const int WS_SYSMENU = 0x00080000;
[DllImport("user32.dll")]
private static extern int SetWindowLong(IntPtr hwnd, int index, int value);
[DllImport("user32.dll")]
private static extern int GetWindowLong(IntPtr hwnd, int index);
public void Connect(int connectionId, object target)
{
throw new NotImplementedException();
}
}
}

View File

@@ -0,0 +1,12 @@
using System.Windows;
using Microsoft.Practices.Prism.Interactivity.InteractionRequest;
namespace DTS.Common.Dialogs
{
public interface IPopupWindowActionAware
{
Window HostWindow { get; set; }
Notification HostNotification { get; set; }
}
}

View File

@@ -0,0 +1,9 @@
using Microsoft.Practices.Prism.Regions;
namespace DTS.Common.Dialogs
{
public interface IRegionManagerAware
{
IRegionManager RegionManager { get; set; }
}
}

View File

@@ -0,0 +1,73 @@
<Window x:Class="DTS.Common.Dialogs.NotificationWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
xmlns:ei="http://schemas.microsoft.com/expression/2010/interactions"
Title="{Binding Title}" Icon="{Binding ImageUri, ElementName=notificationWindow}" WindowStyle="ToolWindow" ResizeMode="NoResize"
SizeToContent="WidthAndHeight" MinWidth="300" MaxWidth="500" MinHeight="150" MaxHeight="500"
x:Name="notificationWindow">
<Window.Resources>
<Style x:Key="ReadOnlyTextBox" TargetType="TextBox">
<Setter Property="IsReadOnly" Value="True" />
<Setter Property="TextWrapping" Value="Wrap" />
<Setter Property="Height" Value="Auto"/>
<Setter Property="Width" Value="Auto"/>
<Setter Property="Margin" Value="5"/>
<Setter Property="Background" Value="White"/>
<Setter Property="BorderBrush" Value="White"/>
<Setter Property="BorderThickness" Value="0"/>
<Setter Property="IsTabStop" Value="False"/>
<Setter Property="TextAlignment" Value="Left"/>
<Setter Property="HorizontalAlignment" Value="Left"/>
<Setter Property="VerticalAlignment" Value="Center"/>
<Setter Property="HorizontalScrollBarVisibility" Value="Hidden"/>
<Setter Property="VerticalScrollBarVisibility" Value="Auto"/>
</Style>
<Style x:Key="LinkButton" TargetType="Button">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Button">
<TextBlock TextDecorations="Underline">
<ContentPresenter />
</TextBlock>
</ControlTemplate>
</Setter.Value>
</Setter>
<Setter Property="Foreground" Value="Blue" />
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="true">
<Setter Property="Foreground" Value="Red" />
</Trigger>
</Style.Triggers>
</Style>
</Window.Resources>
<Grid x:Name="LayoutRoot" Margin="2">
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="48"/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<Image Grid.Row="0" Grid.Column="0" Source="{Binding ImageUri, ElementName=notificationWindow}" VerticalAlignment="Center" HorizontalAlignment="Center" Margin="5,5,0,5"/>
<ContentControl Grid.Row="0" Grid.Column="1" ContentTemplate="{Binding NotificationTemplate, ElementName=notificationWindow}" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Margin="0,10,10,5">
<TextBox Text="{Binding Content.Message, Mode=OneWay}" Style="{StaticResource ReadOnlyTextBox}"/>
</ContentControl>
<StackPanel Grid.Column="0" Grid.Row="1" Grid.ColumnSpan="2" Orientation="Horizontal" HorizontalAlignment="Right" Margin="5,5,10,5">
<Button Style="{StaticResource LinkButton}" Content="Copy to clipboard" Margin="0,5,20,0" HorizontalAlignment="Center" Click="CoppyToClibord_Click"/>
<Button x:Name="OKButton" Content="OK" Width="70" Height="25" Cursor="Hand" HorizontalAlignment="Right">
<i:Interaction.Triggers>
<i:EventTrigger EventName="Click">
<ei:CallMethodAction TargetObject="{Binding ElementName=notificationWindow}" MethodName="Close"/>
</i:EventTrigger>
</i:Interaction.Triggers>
</Button>
</StackPanel>
</Grid>
</Window>

View File

@@ -0,0 +1,72 @@
using System;
using System.Windows;
using System.Runtime.InteropServices;
using System.Windows.Interop;
namespace DTS.Common.Dialogs
{
/// <summary>
/// Interaction logic for NotificationWindow.xaml
/// </summary>
public partial class NotificationWindow : Window
{
public static readonly DependencyProperty NotificationTemplateProperty = DependencyProperty.Register("NotificationTemplate", typeof(DataTemplate), typeof(NotificationWindow), new PropertyMetadata(null));
public static readonly DependencyProperty ImageUriProperty = DependencyProperty.Register("ImageUri", typeof(Uri), typeof(NotificationWindow), new PropertyMetadata(new Uri("pack://application:,,,/" + System.Reflection.Assembly.GetExecutingAssembly() + ";component/RibbonControl/Images/warning_48.png", UriKind.RelativeOrAbsolute)));
/// <summary>
/// Creates a new instance of <see cref="NotificationWindow"/>
/// </summary>
public NotificationWindow()
{
// Hook the SourceInitialized event and then using the SetWindowLong function to strip off the WS_SYSMENU bit.
SourceInitialized += NotificationWindow_SourceInitialized;
InitializeComponent();
}
/// <summary>
/// The <see cref="DataTemplate"/> to apply when displaying <see cref="Microsoft.Practices.Prism.Interactivity.InteractionRequest.Notification"/> data.
/// </summary>
public DataTemplate NotificationTemplate
{
get => (DataTemplate)GetValue(NotificationTemplateProperty);
set => SetValue(NotificationTemplateProperty, value);
}
/// <summary>
/// Gets or sets the <see cref="Uri"/> for the notification image.
/// </summary>
public Uri ImageUri
{
get => (Uri)GetValue(ImageUriProperty);
set => SetValue(ImageUriProperty, value);
}
void NotificationWindow_SourceInitialized(object sender, EventArgs e)
{
// Removing the Close Button on a WPF window.
// The key here is hooking the SourceInitialized event and then using the SetWindowLong function to strip off the WS_SYSMENU bit.
var wih = new WindowInteropHelper(this);
var style = GetWindowLong(wih.Handle, GWL_STYLE);
SetWindowLong(wih.Handle, GWL_STYLE, style & ~WS_SYSMENU);
}
private const int GWL_STYLE = -16;
private const int WS_SYSMENU = 0x00080000;
[DllImport("user32.dll")]
private static extern int SetWindowLong(IntPtr hwnd, int index, int value);
[DllImport("user32.dll")]
private static extern int GetWindowLong(IntPtr hwnd, int index);
public void Connect(int connectionId, object target)
{
}
private void CoppyToClibord_Click(object sender, RoutedEventArgs e)
{
Clipboard.SetText("Hello, clipboard");
}
}
}

View File

@@ -0,0 +1,399 @@
using System;
using System.Windows;
using System.Windows.Interactivity;
using DTS.Common.Enums;
using Microsoft.Practices.Prism.Interactivity.InteractionRequest;
using Microsoft.Practices.Prism.Regions;
using DTS.Common.Events;
namespace DTS.Common.Dialogs
{
/// <summary>
/// Shows a popup window in response to an <see cref="Microsoft.Practices.Prism.Interactivity.InteractionRequest"/> being raised.
/// </summary>
public class PopupWindowAction : TriggerAction<FrameworkElement>
{
private Window _openedNotificationWindow;
#region DependecyProperties
public enum WindowPositions
{
CenterOwner,
CenterScreen
}
//public enum ResultEnum
//{
// Ok = MessageBoxResult.OK,
// Yes = MessageBoxResult.Yes,
// No = MessageBoxResult.No,
// Cancel = MessageBoxResult.Cancel
//}
/// <summary>
/// The content of the child window to display as part of the popup.
/// </summary>
public static readonly DependencyProperty WindowContentProperty =
DependencyProperty.Register("WindowContent", typeof(FrameworkElement), typeof(PopupWindowAction), new PropertyMetadata(null));
/// <summary>
/// The <see cref="DataTemplate"/> to apply to the popup content.
/// </summary>
public static readonly DependencyProperty ContentTemplateProperty =
DependencyProperty.Register("ContentTemplate", typeof(DataTemplate), typeof(PopupWindowAction), new PropertyMetadata(null));
/// <summary>
/// Determines if the content should be shown in a modal window or not.
/// </summary>
public static readonly DependencyProperty IsModalProperty =
DependencyProperty.Register("IsModal", typeof(bool), typeof(PopupWindowAction), new PropertyMetadata(null));
/// <summary>
/// Determines whether the window is shown centered over associated object or entire screen.
/// </summary>
public static readonly DependencyProperty StartupPositionProperty =
DependencyProperty.Register("StartupPosition", typeof(WindowPositions), typeof(PopupWindowAction), new PropertyMetadata(WindowPositions.CenterScreen));
/// <summary>
/// Determines if the content should be initially shown centered over the View that raised the interaction request or not.
/// </summary>
public static readonly DependencyProperty CenterOverAssociatedObjectProperty =
DependencyProperty.Register("CenterOverAssociatedObject", typeof(bool), typeof(PopupWindowAction), new PropertyMetadata(null));
/// <summary>
/// Determines whether the multiple Notification Windows should be allowed.
/// </summary>
public static readonly DependencyProperty AllowMultipleNotificationWindowsProperty =
DependencyProperty.Register("AllowMultipleNotificationWindows", typeof(bool), typeof(PopupWindowAction), new PropertyMetadata(null));
///// <summary>
///// Determines timeout interval in milliseconds (0 = no timeout).
///// </summary>
//public static readonly DependencyProperty TimeoutIntervalProperty =
// DependencyProperty.Register("AutoCancelInterval", typeof(int), typeof(PopupWindowAction), new PropertyMetadata(0));
///// <summary>
///// Determines result if timeout occurs.
///// </summary>
//public static readonly ResultEnum TimeoutResultProperty =
// DependencyProperty.Register("TimeoutResult", typeof(ResultEnum), typeof(PopupWindowAction), new PropertyMetadata(ResultEnum.Cancel));
#endregion
#region Getters and Setters
/// <summary>
/// Gets or sets the content of the window.
/// </summary>
public FrameworkElement WindowContent
{
get => (FrameworkElement)GetValue(WindowContentProperty);
set => SetValue(WindowContentProperty, value);
}
/// <summary>
/// Gets or sets the content template for the window.
/// </summary>
public DataTemplate ContentTemplate
{
get => (DataTemplate)GetValue(ContentTemplateProperty);
set => SetValue(ContentTemplateProperty, value);
}
/// <summary>
/// Gets or sets a value indicating whether the window will be modal or not.
/// </summary>
public bool IsModal
{
get => (bool)GetValue(IsModalProperty);
set => SetValue(IsModalProperty, value);
}
/// <summary>
/// Gets or sets a value whether the window is shown centered over associated object, the screen or WindowsDefault.
/// </summary>
public WindowPositions StartupPosition
{
get => (WindowPositions)GetValue(StartupPositionProperty);
set => SetValue(StartupPositionProperty, value);
}
/// <summary>
/// Gets or sets a value whether the multiple Notification Windows should be allowed.
/// This property can be used for the popup window of type <see cref="NotificationWindow"/> only.
/// </summary>
public bool AllowMultipleNotificationWindows
{
get => (bool)GetValue(AllowMultipleNotificationWindowsProperty);
set => SetValue(AllowMultipleNotificationWindowsProperty, value);
}
///// <summary>
///// Gets or sets a value indicating number of milliseconds before dialog times out (0 = no timeout).
///// </summary>
//public int TimeoutInterval
//{
// get { return (int)GetValue(TimeoutIntervalProperty); }
// set { SetValue(TimeoutIntervalProperty, value); }
//}
///// <summary>
///// Gets or sets a value indicating number of milliseconds before dialog times out (0 = no timeout).
///// </summary>
//public ResultEnum TimeoutResult
//{
// get { return (ResultEnum)GetValue(TimeoutResultProperty); }
// set { SetValue(TimeoutResultProperty, value); }
//}
#endregion
#region PopupWindowAction logic
/// <summary>
/// Displays the child window and collects results for <see cref="IInteractionRequest"/>.
/// </summary>
/// <param name="parameter">The parameter to the action. If the action does not require a parameter, the parameter may be set to a null reference.</param>
protected override void Invoke(object parameter)
{
var args = parameter as InteractionRequestedEventArgs;
if (args == null)
return;
// If the WindowContent shouldn't be part of another visual tree.
if (WindowContent != null && WindowContent.Parent != null)
return;
var wrapperWindow = GetWindow(args.Context);
if (wrapperWindow == null)
return;
var callback = args.Callback;
EventHandler handler = null;
handler =
(o, e) =>
{
wrapperWindow.Closed -= handler;
wrapperWindow.Content = null;
if (_openedNotificationWindow != null && wrapperWindow is NotificationWindow)
_openedNotificationWindow = null;
callback();
};
wrapperWindow.Closed += handler;
// New way, using StartupPosition enumeration:
//
if (StartupPosition == WindowPositions.CenterOwner || StartupPosition == WindowPositions.CenterScreen)
{
SizeChangedEventHandler sizeHandler = null;
sizeHandler =
(o, e) =>
{
wrapperWindow.SizeChanged -= sizeHandler;
if (StartupPosition == WindowPositions.CenterOwner)
{
var invoker = AssociatedObject;
var position = invoker.PointToScreen(new Point(0, 0));
wrapperWindow.Top = position.Y + ((invoker.ActualHeight - wrapperWindow.ActualHeight) / 2);
wrapperWindow.Left = position.X + ((invoker.ActualWidth - wrapperWindow.ActualWidth) / 2);
}
else
{
wrapperWindow.Top = ((SystemParameters.WorkArea.Height - wrapperWindow.ActualHeight) / 2);
wrapperWindow.Left = ((SystemParameters.WorkArea.Width - wrapperWindow.ActualWidth) / 2);
}
};
wrapperWindow.SizeChanged += sizeHandler;
}
// Old way, using CenterOverAssociatedObject property:
//
//if (CenterOverAssociatedObject)
//{
// SizeChangedEventHandler sizeHandler = null;
// sizeHandler =
// (o, e) =>
// {
// wrapperWindow.SizeChanged -= sizeHandler;
// FrameworkElement invoker = AssociatedObject;
// Point position = invoker.PointToScreen(new Point(0, 0));
// wrapperWindow.Top = position.Y + ((invoker.ActualHeight - wrapperWindow.ActualHeight) / 2);
// wrapperWindow.Left = position.X + ((invoker.ActualWidth - wrapperWindow.ActualWidth) / 2);
// };
// wrapperWindow.SizeChanged += sizeHandler;
//}
if (AllowMultipleNotificationWindows == false && wrapperWindow is NotificationWindow)
_openedNotificationWindow = wrapperWindow;
if (IsModal)
{
wrapperWindow.ShowDialog();
}
else
{
wrapperWindow.Show();
}
}
/// <summary>
/// Checks if the WindowContent or its DataContext implements IPopupWindowActionAware and IRegionManagerAware.
/// If so, it sets the corresponding values.
/// Also, if WindowContent does not have a RegionManager attached, it creates a new scoped RegionManager for it.
/// </summary>
/// <param name="notification">The notification to be set as a DataContext in the HostWindow.</param>
/// <param name="wrapperWindow">The HostWindow</param>
protected void PrepareContentForWindow(Notification notification, Window wrapperWindow)
{
if (WindowContent == null)
return;
// We set the WindowContent as the content of the window.
wrapperWindow.Content = WindowContent;
var regionManager = WindowContent.GetValue(RegionManager.RegionManagerProperty) as IRegionManager;
// If the WindowContent does not have a RegionManager attached we create a new scoped RegionManager for it in order to support regions.
if (regionManager == null)
{
regionManager = new RegionManager();
WindowContent.SetValue(RegionManager.RegionManagerProperty, regionManager);
}
// If the WindowContent implements IRegionManagerAware we set the new scoped manager as the RegionManager.
var regionManagerAwareContent = WindowContent as IRegionManagerAware;
if (regionManagerAwareContent != null)
{
regionManagerAwareContent.RegionManager = regionManager;
}
// If the WindowContent's DataContext implements IRegionManagerAware we set the new scoped manager as the RegionManager.
var regionManagerAwareDataContext = WindowContent.DataContext as IRegionManagerAware;
if (regionManagerAwareDataContext != null)
{
regionManagerAwareDataContext.RegionManager = regionManager;
}
// If the WindowContent implements IPopupWindowActionAware, we set the corresponding values.
var popupAwareContent = WindowContent as IPopupWindowActionAware;
if (popupAwareContent != null)
{
popupAwareContent.HostWindow = wrapperWindow;
popupAwareContent.HostNotification = notification;
}
// If the WindowContent's DataContext implements IPopupWindowActionAware, we set the corresponding values.
var popupAwareDataContext = WindowContent.DataContext as IPopupWindowActionAware;
if (popupAwareDataContext != null)
{
popupAwareDataContext.HostWindow = wrapperWindow;
popupAwareDataContext.HostNotification = notification;
}
}
#endregion
#region Window creation methods
/// <summary>
/// Returns the window to display as part of the trigger action.
/// </summary>
/// <param name="notification">The notification to be set as a DataContext in the window.</param>
/// <returns></returns>
protected Window GetWindow(Notification notification)
{
Window wrapperWindow;
if (WindowContent != null)
{
wrapperWindow = new Window();
// If the WindowContent does not have its own DataContext, it will inherit this one.
wrapperWindow.DataContext = notification;
wrapperWindow.Title = notification.Title;
PrepareContentForWindow(notification, wrapperWindow);
}
else
{
wrapperWindow = CreateWindow(notification);
}
return wrapperWindow;
}
/// <summary>
/// When no WindowContent is sent this method is used to create a Notification/Confirmation window to show
/// the corresponding <see cref="Notification"/> or <see cref="Confirmation"/>.
/// </summary>
/// <param name="notification">The Notification or Confirmation parameter to show.</param>
/// <returns></returns>
protected Window CreateWindow(Notification notification)
{
Window window = null;
if (notification is Confirmation)
{
window = new ConfirmationWindow { ConfirmationTemplate = ContentTemplate };
}
else
{
var content = notification.Content as NotificationContentEventArgs;
if (content == null)
return null;
var imageUri = GetImageUri(content.Image);
window = new NotificationWindow { NotificationTemplate = ContentTemplate, ImageUri = imageUri };
}
window.DataContext = notification;
return window;
}
/// <summary>
/// Gets the <see cref="Uri"/> for the notification image.
/// </summary>
/// <param name="windowImage">The popup window's symbol.</param>
private Uri GetImageUri(PopupWindowImage windowImage)
{
string image;
switch (windowImage)
{
case PopupWindowImage.Error:
image = "error_48.png";
break;
case PopupWindowImage.Question:
image = "question_48.png";
break;
case PopupWindowImage.Warning:
image = "warning_48.png";
break;
case PopupWindowImage.Information:
image = "information_48.png";
break;
default:
image = "warning_48.png";
break;
}
return new Uri("pack://application:,,,/" + System.Reflection.Assembly.GetExecutingAssembly() + ";component/Images/" + image, UriKind.RelativeOrAbsolute);
}
#endregion
}
}