This is the xaml "SidiMessageBoxWindow.xaml" file:
<Window
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008" mc:Ignorable="d">
<Border>
<Grid x:Name="mainGrid" Margin="0">
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<ScrollViewer Grid.Row="0" VerticalScrollBarVisibility="Auto" Margin="0">
<TextBlock x:Name="TextBlock" TextWrapping="Wrap" Text="TextBlock" ScrollViewer.VerticalScrollBarVisibility="Auto" Height="Auto"/>
</ScrollViewer>
<Grid Grid.Row="1" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Height="Auto" Width="Auto" Margin="0,10,0,0">
<Button x:Name="btnCancel" Content="Cancel" Width="Auto" MinWidth="0" Height="30"
HorizontalAlignment="Right" HorizontalContentAlignment="Center" VerticalAlignment="Center" VerticalContentAlignment="Center"
Margin="0,0,45,0" Padding="4,0,4,0" BorderThickness="1" />
<Button x:Name="btnOk" Content="Ok" Width="Auto" MinWidth="40" Height=" 30"
HorizontalAlignment="Right" HorizontalContentAlignment="Center" VerticalAlignment="Center" VerticalContentAlignment="Center"
Margin="0" Padding="4,0,4,0" BorderThickness="1" />
</Grid>
</Grid>
</Border>
and here the "SidiMessageBox1" class
I can't use "messageBox.ShowDialog();" here because I have to use the class "NTWindow" which I don't have access to.
public class SidiMessageBox1 : SidiMessageBoxWindow1
{
public static MessageBoxResult Show(ChartControl chartControl, string text, MessageBoxButton buttons = MessageBoxButton.OK)
{
if (chartControl == null)
{
return MessageBoxResult.None;
}
var messageBox = CreateMessageBox(chartControl, text, buttons);
messageBox.Show();
return messageBox.MsgBoxResult;
}
private static SidiMessageBoxWindow1 CreateMessageBox(ChartControl chartControl, string text, MessageBoxButton buttons)
{
return new SidiMessageBoxWindow1(text, buttons)
{
Owner = chartControl.OwnerChart,
Foreground = Application.Current.TryFindResource("FontControlBrush") as SolidColorBrush
};
}
}
and here is the SidiMessageBoxWindow1 class
public class SidiMessageBoxWindow1 : NTWindow
{
private static readonly string xamlFilePath = Path.Combine(Globals.UserDataDir, #"bin\Custom\AddOns\Sidi\SidiMessageBoxWindow.xaml");
private string text;
private Button btnOk, btnCancel;
private TextBlock textBlock;
private MessageBoxButton buttons;
public SidiMessageBoxWindow1()
{
}
public SidiMessageBoxWindow1(string text, MessageBoxButton buttons)
{
this.text = text;
Caption = "SidiMessageBox";
Topmost = true;
MinHeight = 100;
MinWidth = 200;
ResizeMode = ResizeMode.NoResize;
SizeToContent = SizeToContent.WidthAndHeight;
WindowStartupLocation = WindowStartupLocation.CenterOwner;
Content = LoadXaml(xamlFilePath);
Buttons = buttons;
}
private void OkButton_Click(object sender, RoutedEventArgs e)
{
btnOk.Click -= OkButton_Click;
Close();
}
private void CancelButton_Click(object sender, RoutedEventArgs e)
{
btnCancel.Click -= CancelButton_Click;
Close();
}
private DependencyObject LoadXaml(string xmlFilePath)
{
Window page;
FileStream fs = new FileStream(xmlFilePath, FileMode.Open);
page = (Window)XamlReader.Load(fs);
btnOk = LogicalTreeHelper.FindLogicalNode(page, "btnOk") as Button;
btnCancel = LogicalTreeHelper.FindLogicalNode(page, "btnCancel") as Button;
textBlock = LogicalTreeHelper.FindLogicalNode(page, "TextBlock") as TextBlock;
textBlock.Text = text;
return page.Content as DependencyObject;
}
public MessageBoxButton Buttons
{
get
{
return buttons;
}
set
{
buttons = value;
btnCancel.Visibility = Visibility.Collapsed;
btnOk.Visibility = Visibility.Collapsed;
switch (buttons)
{
case MessageBoxButton.OK:
btnOk.Visibility = Visibility.Visible;
btnOk.Click += OkButton_Click;
break;
case MessageBoxButton.OKCancel:
btnCancel.Visibility = Visibility.Visible;
btnOk.Visibility = Visibility.Visible;
btnOk.Click += OkButton_Click;
btnCancel.Click += CancelButton_Click;
break;
}
}
}
public MessageBoxResult MsgBoxResult { get; set; }
}
call:
var result = SidiMessageBox1.Show(ChartControl, "text");
the messageboxwindow looks like this:
everything works fine, as it should, except that I don't get a "MessageBoxResult" back. Unfortunately I don't know how to do that with this code.
I thank "BionicCode" for his explanation and hope for your understanding, because i am still quite a beginner ;-)
To use the Thread class is considered an obsolete programming model. Since the introduction of async and await with .NET Framework 4.5 the recommended programming model is the Microsoft Docs: Task asynchronous programming model.
As the member name Dispatcher.InvokeAsync suggests, this method is awaitable and supports asynchronous execution.
Your code actually does not execute the Window on a new thread. Because you use post related code to the Dispatcher, the Window is shown on the main thread.
Showing another Window will not block the other Window instances.
Additionally, your posted code is quite smelly. You should never block a constructor. But showing a modal dialog from a constructor will block construction. A constructor must initialize the members and return immediately.
Instead you must create and show the Window instance from your static Show method:
public static MessageBoxResult Show(ChartControl chartControl, string text, MessageBoxButton buttons)
{
if (chartControl == null)
{
return MessageBoxResult.None;
}
SidiMessageBoxWindow messageBox = CreateMessageBox(chartControl, text, buttons);
// If just an OK button, allow the user to just move away from the dialog
if (buttons == MessageBoxButton.OK)
{
messageBox.Show();
}
else
{
messageBox.ShowDialog();
}
return messageBox.MsgBoxResult;
}
private static SidiMessageBoxWindow CreateMessageBox(ChartControl chartControl, string text, MessageBoxButton buttons)
{
return new SidiMessageBoxWindow(xamlFilePath, logFilePath, text, buttons, chartControl)
{
Owner = chartControl.OwnerChart,
Foreground = Application.Current.TryFindResource("FontControlBrush") as SolidColorBrush
};
}
private SidiMessageBoxWindow(ChartControl chartControl, string text, MessageBoxButton buttons)
{
this.chartControl = chartControl;
this.text = text;
this.buttons = buttons;
}
Related
I am an experienced WinForms developer, relatively new to WPF. I have a large WinForms application that uses a couple different base classes to represent dialog boxes. One such example is AbstractOkCancelDialog. That class contains a panel at the bottom of a dialog, with an Ok and Cancel button on the right side of the panel. I'm trying to determine the best way to handle this, as I realize that WPF doesn't provide visual inheritance.
I don't want to have to create OK and Cancel buttons, and place them, for every dialog in the application.
I have read that the way to do this in WPF is with user controls. I can envision creating a user control with OK and Cancel buttons on it. But I don't want to have to manually place that user control on hundreds of dialogs in my application. I'd really like to have something like this:
public AbstractOkCancelDialog = class(Window)
{
protected AbstractOkCancelDialogViewModel _ViewModel;
// AbstractOkCancelDialogViewModel would have commands for OK and Cancel.
// Every dialog would inherit from AbstractOkCancelDialog, and would use
// a viewmodel that inherits from AbstractOkCancelDialogViewModel. In
// this way, all view models would automatically be connected to the OK
// and Cancel commands.
}
I've seen some discussion online about how to create the base class. Those discussions explain how there can't be a xaml file associated with the dialog base class, and I understand that restriction. I just can't figure out how to automatically place the user control with the OK and Cancel buttons.
I'm hoping that someone can point me to a sample solution that shows this kind of structure. Thank you in advance!
Write one dialog class. It's a subclass of Window. It has XAML:
<Window
...blah blah blah...
Title="{Binding Title}"
>
<StackPanel MinWidth="300">
<!-- This is how you place content within content in WPF -->
<ContentControl
Content="{Binding}"
Margin="2"
/>
<StackPanel Orientation="Horizontal" HorizontalAlignment="Right" Margin="2,20,2,2">
<Button
Margin="2"
MinWidth="60"
DockPanel.Dock="Right"
Content="OK"
Click="OK_Click"
IsDefault="True"
/>
<Button
Margin="2"
MinWidth="60"
DockPanel.Dock="Right"
Content="Cancel"
IsCancel="True"
Click="Cancel_Click"
/>
</StackPanel>
</StackPanel>
</Window>
You can fancy that up endlessly, but this is a decent minimum to give you arbitrary content above a row of right-aligned buttons. Adding more buttons as needed could involve either templating that portion of the window as well, or creating them with an ItemsControl (I've done that in our production code), or a few other options.
Usage:
var vm = new SomeDialogViewModel();
var dlg = new MyDialog { DataContext = vm };
For each dialog viewmodel, consumers must define an implicit datatemplate which provides UI for that viewmodel.
I would suggest writing a dialog viewmodel interface which the consumer is expected to implement.
public interface IDialogViewModel
{
String Title { get; set; }
void OnOK();
// Let them "cancel the cancel" if they like.
bool OnCancel();
}
The window can check if its DataContext implements that interface, and act accordingly. If you like, it could require that interface and throw an exception of it isn't implemented, or it could just talk to it only if it's there. If they don't implement it but they still have a Title property, the binding to Title will still work. Bindings are "duck typed".
Naturally, you can write an OKCancelDialogViewModel or a SelectStringFromListViewModel, write corresponding DataTemplates that implement their UIs, and write nice clean static methods which show them:
public static class Dialogs
{
public static TOption Select<TOption>(IEnumerable<TOption> options, string prompt,
string title = "Select Option") where TOption : class
{
// Viewmodel isn't generic because that breaks implicit datatemplating.
// That's OK because XAML uses duck typing anyhow.
var vm = new SelectOptionDialogViewModel
{
Title = title,
Prompt = prompt,
Options = options
};
if ((bool)new Dialog { DataContext = vm }.ShowDialog())
{
return vm.SelectedOption as TOption;
}
return null;
}
// We have to call the value-type overload by a different name because overloads can't be
// distinguished when the only distinction is a type constraint.
public static TOption? SelectValue<TOption>(IEnumerable<TOption> options, string prompt,
string title = "Select Option") where TOption : struct
{
var vm = new SelectOptionDialogViewModel
{
Title = title,
Prompt = prompt,
// Need to box these explicitly
Options = options.Select(opt => (object)opt)
};
if ((bool)new Dialog { DataContext = vm }.ShowDialog())
{
return (TOption)vm.SelectedOption;
}
return null;
}
}
Here's a viewmodel datatemplate for the above selection dialog:
<Application.Resources>
<DataTemplate DataType="{x:Type local:SelectOptionDialogViewModel}">
<StackPanel>
<TextBlock
TextWrapping="WrapWithOverflow"
Text="{Binding Prompt}"
/>
<ListBox
ItemsSource="{Binding Options}"
SelectedItem="{Binding SelectedOption}"
MouseDoubleClick="ListBox_MouseDoubleClick"
/>
</StackPanel>
</DataTemplate>
</Application.Resources>
App.xaml.cs
private void ListBox_MouseDoubleClick(object sender,
System.Windows.Input.MouseButtonEventArgs e)
{
((sender as FrameworkElement).DataContext as IDialogViewModel).DialogResult = true;
}
var a = Dialogs.Select(new String[] { "Bob", "Fred", "Ginger", "Mary Anne" },
"Select a dance partner:");
var b = Dialogs.SelectValue(Enum.GetValues(typeof(Options)).Cast<Options>(),
"Select an enum value:");
Here an example of how to use a custom AlertDialog
UserControl
<UserControl x:Class="Library.Views.AlertMessageDialogView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:p="clr-namespace:Library.Properties"
DataContext="{Binding RelativeSource={RelativeSource Self}}"
FlowDirection = "{Binding WindowFlowDirection, Mode=TwoWay}">
<Grid Background="{DynamicResource WindowBackgroundBrush}">
<Canvas HorizontalAlignment="Left" Height="145" VerticalAlignment="Top" Width="385">
<Label HorizontalAlignment="Left" Height="57" VerticalAlignment="Top" Width="365" Canvas.Left="10" Canvas.Top="10" FontSize="14" >
<TextBlock x:Name="txtVocabAnglais" TextWrapping="Wrap" Text="{Binding Message, Mode=TwoWay}" Width="365" Height="57" />
</Label>
<Button x:Name="cmdCancel" Content="{x:Static p:Resources.AlertMessageDialogViewcmdCancel}" Height="30" Canvas.Left="163" Canvas.Top="72" Width="71" Command = "{Binding CancelCommand}" CommandParameter = "null" IsDefault="True"/>
</Canvas>
</Grid>
</UserControl>
ViewModel Class
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.IO;
using System.Windows;
using System.ComponentModel;
using System.Windows.Controls;
namespace Library.ViewModel
{
public class AlertMessageDialogViewModel : BindableBaseViewModel
{
public event EventHandler CloseWindowEvent;
private string _title;
private string _message;
public BaseCommand<string> YesCommand { get; private set; }
public BaseCommand<string> CancelCommand { get; private set; }
private WinformsNameSpace.FlowDirection _windowFlowDirection;
public AlertMessageDialogViewModel()
{
CancelCommand = new BaseCommand<string>(cmdCancelBtnClick);
WindowFlowDirection = CustomFuncVar.WindowFlowDirection;
}
public WinformsNameSpace.FlowDirection WindowFlowDirection
{
get
{
return _windowFlowDirection;
}
set
{
_windowFlowDirection = value;
OnPropertyChanged("WindowFlowDirection");
}
}
public string Message
{
get
{
return _message;
}
set
{
_message = value;
OnPropertyChanged("Message");
}
}
public string Title
{
get
{
return _title;
}
set
{
_title = value;
}
}
private void cmdCancelBtnClick(string paramerter)
{
if (CloseWindowEvent != null)
CloseWindowEvent(this, null);
}
}
}
DialogMessage Class
using System;
using System.Windows;
using System.Collections.Generic;
namespace Library.Helpers
{
public static class DialogMessage
{
public static void AlertMessage(string message, string title, Window OwnerWindowView)
{
try
{
//Affichage de méssage de succès enregistrement
AlertMessageDialogViewModel alertDialogVM = new AlertMessageDialogViewModel();
alertDialogVM.Message = message;
alertDialogVM.Title = title;
// Auto Generation Window
FrameworkElement view = LpgetCustomUI.AutoGetViewFromName("AlertMessageDialogView");
view.DataContext = alertDialogVM;
Dictionary<string, object> localVarWindowProperty = new Dictionary<string, object>();
localVarWindowProperty = LpgetCustomUI.GetWindowPropretyType_400x145(Properties.Resources.ApplicationTitle);
CummonUIWindowContainer alertDialogView = new CummonUIWindowContainer(view, null, false, localVarWindowProperty);
//End Auto Generation Window
// Attachement de l'évènement de fermture de View au modèle
alertDialogVM.CloseWindowEvent += new EventHandler(alertDialogView.fnCloseWindowEvent);
if (OwnerWindowView!=null)
{
alertDialogView.Owner = OwnerWindowView;
}
else
{
alertDialogView.WindowStartupLocation = WindowStartupLocation.CenterScreen;
}
alertDialogView.ShowDialog();
}
catch (Exception ex)
{
}
}
}
}
CummonUIWindowContainer Class
namespace CummonUILibrary.CummonUIHelpers
{
public class CummonUIWindowContainer : Window
{
public event RoutedEventHandler CmbRootEvtLanguageChange;
private FrameworkElement currentView;
private ContentControl _contentcontainer;
public CummonUIWindowContainer(string usercontrolName)
{
Contentcontainer = new ContentControl();
currentView = new FrameworkElement();
}
public CummonUIWindowContainer()
{
Contentcontainer = new ContentControl();
currentView = new FrameworkElement();
}
public CummonUIWindowContainer(FrameworkElement view, object model, bool setDataContextToView, Dictionary<string, object> WindowPropertyList)
{
Contentcontainer = new ContentControl();
Contentcontainer.Name = "ContentControl";
SetWindowProperty(view, model, setDataContextToView, WindowPropertyList);
}
public void SetWindowProperty(FrameworkElement view, object model, bool setDataContextToView, Dictionary<string, object> WindowPropertyList)
{
try
{
LinearGradientBrush brush = new LinearGradientBrush();
GradientStop gradientStop1 = new GradientStop();
gradientStop1.Offset = 0;
gradientStop1.Color = Colors.Yellow;
brush.GradientStops.Add(gradientStop1);
GradientStop gradientStop2 = new GradientStop();
gradientStop2.Offset = 0.5;
gradientStop2.Color = Colors.Indigo;
brush.GradientStops.Add(gradientStop2);
GradientStop gradientStop3 = new GradientStop();
gradientStop3.Offset = 1;
gradientStop3.Color = Colors.Yellow;
brush.GradientStops.Add(gradientStop3);
this.Background = brush;
CurrentView = view;
Type elementType = this.GetType();
ICollection<string> WindowPropertyListNames = WindowPropertyList.Keys;
foreach (string propertyName in WindowPropertyListNames)
{
PropertyInfo property = elementType.GetProperty(propertyName);
property.SetValue(this, WindowPropertyList[propertyName]);
}
if (setDataContextToView == true & model != null)
{
CurrentView.DataContext = model;
}
if (CurrentView != null)
{
Contentcontainer.Content = CurrentView;
}
//Contentcontainer.Margin = new Thickness(0,0, 0, 0);
IAddChild container=this;
container.AddChild(Contentcontainer);
}
catch (Exception ex)
{
}
}
public void fnCloseWindowEvent(object sender, EventArgs e)
{
this.Close();
}
public ContentControl Contentcontainer
{
get
{
return _contentcontainer;
}
set
{
_contentcontainer = value;
}
}
public FrameworkElement CurrentView
{
get
{
return currentView;
}
set
{
if (this.currentView != value)
{
currentView = value;
//RaisePropertyChanged("CurrentView");
}
}
}
private void cmbLanguage_SelectionChanged(object sender, RoutedEventArgs e)
{
//CmbRootEvtLanguageChange(sender, e);
}
}
}
How to use the Class
DialogMessage.AlertMessage("My Custom Message", "My Custom Title Message");
Thats how i would do it
Create an abstract base class for your dialog and changing the corresponding ControlTemplate
AbstractOkCancelDialog
public abstract class AbstractOkCancelDialog : Window
{
public static readonly DependencyProperty CancelCommandParameterProperty =
DependencyProperty.Register(
"CancelCommandParameter",
typeof(object),
typeof(AbstractOkCancelDialog),
new FrameworkPropertyMetadata((object) null));
public static readonly DependencyProperty CancelCommandProperty =
DependencyProperty.Register(
"CancelCommand",
typeof(ICommand),
typeof(AbstractOkCancelDialog),
new FrameworkPropertyMetadata((ICommand) null));
public static readonly DependencyProperty OkCommandParameterProperty =
DependencyProperty.Register(
"OkCommandParameter",
typeof(object),
typeof(AbstractOkCancelDialog),
new FrameworkPropertyMetadata((object) null));
public static readonly DependencyProperty OkCommandProperty =
DependencyProperty.Register(
"OkCommand",
typeof(ICommand),
typeof(AbstractOkCancelDialog),
new FrameworkPropertyMetadata((ICommand) null));
static AbstractOkCancelDialog()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(AbstractOkCancelDialog), new
FrameworkPropertyMetadata(typeof(AbstractOkCancelDialog)));
}
public ICommand CancelCommand
{
get => (ICommand) GetValue(CancelCommandProperty);
set => SetValue(CancelCommandProperty, value);
}
public object CancelCommandParameter
{
get => GetValue(CancelCommandParameterProperty);
set => SetValue(CancelCommandParameterProperty, value);
}
public ICommand OkCommand
{
get => (ICommand) GetValue(OkCommandProperty);
set => SetValue(OkCommandProperty, value);
}
public object OkCommandParameter
{
get => GetValue(OkCommandParameterProperty);
set => SetValue(OkCommandParameterProperty, value);
}
}
Style
Put in Generic.xaml[?]
<Style
BasedOn="{StaticResource {x:Type Window}}"
TargetType="{x:Type local:AbstractOkCancelDialog}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type local:AbstractOkCancelDialog}">
<Border
Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}">
<AdornerDecorator>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<ContentPresenter />
<Grid Grid.Row="1">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<Button
Grid.Column="1"
Margin="5"
Command="{TemplateBinding OkCommand}"
CommandParameter="{TemplateBinding OkCommandParameter}"
Content="Ok"
DockPanel.Dock="Right" />
<Button
Grid.Column="2"
Margin="5"
Command="{TemplateBinding CancelCommand}"
CommandParameter="{TemplateBinding CancelCommandParameter}"
Content="Cancel"
DockPanel.Dock="Right" />
</Grid>
</Grid>
</AdornerDecorator>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
Now you can create your individual dialogs like you would create any other window
Brief example:
TestDialog.xaml
<local:AbstractOkCancelDialog
x:Class="WpfApp.TestDialog"
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:local="clr-namespace:WpfApp"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
Title="TestDialog"
Width="800"
Height="450"
OkCommand="{x:Static local:Commands.OkWindowCommand}"
OkCommandParameter="{Binding RelativeSource={RelativeSource Self}}"
CancelCommand="{x:Static local:Commands.CancelWindowCommand}"
CancelCommandParameter="{Binding RelativeSource={RelativeSource Self}}"
mc:Ignorable="d">
<Grid>
<!-- Content -->
</Grid>
</local:AbstractOkCancelDialog>
TestDialog.xaml.cs
public partial class TestDialog : AbstractOkCancelDialog
{
...
}
I'm implementing a login window before my shell runs. How will I run the code and say that after authentication in the login window, proceed with the execution of shell application.
Here is my initial code:
LoginViewModel.cs
public event EventHandler LoginCompleted;
private void RaiseLoginCompletedEvent()
{
LoginCompleted?.Invoke(this, EventArgs.Empty);
}
Bootstraper.cs from the Shell
My problem here is that I cannot instantiate my LoginModel since the constructor of my Model has a parameter to use my Services Interface.
Is there any solution for this? Thanks!
New up an instance of a class that implements the ILoginAuth interface yourself:
var loginVM = new LoginViewModel(new LoginAuth());
...or let the container do it for you:
var loginVM = new LoginViewModel(Container.Resolve<ILoginAuth>());
For the latter option to work, you must register your type mappings by overriding the RegisterTypes method:
protected override void RegisterTypes(IContainerRegistry containerRegistry)
{
containerRegistry.Register<ILoginAuth, LoginAuth>();
}
Another method of doing this:
ILoginAuth auth = CommonServiceLocator.ServiceLocator.Current.GetInstance<ILoginAuth>();
This way you don't have to include ILoginAuth in your constructor. Also, you can do this from any where!
<pre>
/*Brian code modified */
public interface ICloseWindow {
Action Close {
get;
set;
}
}
public partial class LoginWindow: Window {
public LoginWindow(ILoginViewModel viewModel) {
InitializeComponent();
DataContext = viewModel;
Loaded += MainWindow_Loaded;
}
private void MainWindow_Loaded(object sender, RoutedEventArgs e) {
if (DataContext is ICloseWindow vm) {
vm.Close += () =>{
DialogResult = true;
};
}
}
private void Close_Window(object sender, RoutedEventArgs e) {
DialogResult = false;
}
}
/*App.xaml.cs*/
protected override void InitializeShell(Window shell) {
Window login = Container.Resolve < LoginWindow > ();
var result = login.ShowDialog();
if (!result.Value) {
Application.Current.Shutdown();
}
else {
base.InitializeShell(shell);
}
}
public class LoginViewModel: BindableBase,
ILoginViewModel,
ICloseWindow {
private string _userName;
private string _password;
private bool _isAuthenticated;
private ICommand _authenticateUserCommand;
private IEventAggregator _eventAggregator;
public Action Close {
get;
set;
}
public ICommand AuthenticateUserCommand {
get =>_authenticateUserCommand;
set =>_authenticateUserCommand = value;
}
public LoginViewModel(IEventAggregator eventAggregator) {
AuthenticateUserCommand = new DelegateCommand < object > (AuthenticateUser);
_eventAggregator = eventAggregator;
}
private void AuthenticateUser(object parameter) {
var passwordBox = parameter as PasswordBox;
var password = passwordBox.Password;
if (password == "password") {
_isAuthenticated = true;
_eventAggregator.GetEvent < MessageSentEvent > ().Publish("Login Data");
}
if (_isAuthenticated) {
Close ? .Invoke();
}
}
public string UserName {
get =>_userName;
set =>_userName = value;
}
public string Password {
get {
return _password;
}
set {
if (_password != value) {
_password = value;
RaisePropertyChanged(nameof(Password));
}
}
}
}
<Window x:Class="YourApplication.Views.LoginWindow"
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"
xmlns:local="clr-namespace:YourApplication"
mc:Ignorable="d"
Title="LoginWindow"
Height="450"
Width="800"
WindowStartupLocation="CenterScreen"
WindowStyle="None"
x:Name="WindowBorder"
RenderOptions.BitmapScalingMode="HighQuality">
<Grid SnapsToDevicePixels="True"
UseLayoutRounding="True"
TextOptions.TextFormattingMode="Display"
TextOptions.TextRenderingMode="ClearType">
<DockPanel>
<DockPanel DockPanel.Dock="Left"
Width="400"
LastChildFill="True">
<Canvas>
<Canvas.Background>
<ImageBrush ImageSource="pack://application:,,,/YourApplication;component/Images/Splash.png" />
</Canvas.Background>
<TextBlock Margin="4,0,0,4"
VerticalAlignment="Center"
Foreground="#FFAAAAAA"
TextWrapping="NoWrap"
Text="Library Management System"
FontSize="16"
Background="Transparent"
DockPanel.Dock="Top" />
</Canvas>
</DockPanel>
<DockPanel LastChildFill="True">
<Button x:Name="PART_CLOSE"
DockPanel.Dock="Top"
HorizontalAlignment="Right"
VerticalAlignment="Center"
Margin="5"
Height="20"
Width="20"
Style="{DynamicResource MetroWindowButtonStyle}"
Click="Close_Window">
<Path Data="F1M54.0573,47.8776L38.1771,31.9974 54.0547,16.1198C55.7604,14.4141 55.7604,11.6511 54.0573,9.94531 52.3516,8.23962 49.5859,8.23962 47.8802,9.94531L32.0026,25.8229 16.1224,9.94531C14.4167,8.23962 11.6511,8.23962 9.94794,9.94531 8.24219,11.6511 8.24219,14.4141 9.94794,16.1198L25.8255,32 9.94794,47.8776C8.24219,49.5834 8.24219,52.3477 9.94794,54.0534 11.6511,55.7572 14.4167,55.7585 16.1224,54.0534L32.0026,38.1745 47.8802,54.0534C49.5859,55.7585 52.3516,55.7572 54.0573,54.0534 55.7604,52.3477 55.763,49.5834 54.0573,47.8776z"
Stretch="Uniform"
Fill="#FFAAAAAA"
Width="10"
Margin="0,0,0,0"></Path>
</Button>
<StackPanel>
<PasswordBox x:Name="txtPassword"
Margin="2,20,10,10"
Height="22"
Width="100" />
<Button Width="100"
Height="22"
Margin="10,150,10,10"
Content="Login"
Command="{Binding AuthenticateUserCommand}"
CommandParameter="{Binding ElementName=txtPassword}" />
</StackPanel>
</DockPanel>
</DockPanel>
</Grid>
</Window>
</pre>
I have a custom control which shows some statistic data and need always be placed at the top edge of WP7 screen. but, when user inputs something on a textbox, the soft-keyboard popup. And the custom control is moved out of the screen. I want to make sure the custom control always visible, even when soft keyboard is popup. Does anyone know how to do this?
You must to use some "magic". By "magic" I mean RenderTransform.
Solution is simple - you need to move your custom control (down, when keyboard visible; up, when hidden). Check this valuable post - it's must help you.
Regards.
<phone:PhoneApplicationPage
x:Class="Test.Keyboard.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:phone="clr-namespace:Microsoft.Phone.Controls;assembly=Microsoft.Phone"
FontFamily="{StaticResource PhoneFontFamilyNormal}"
FontSize="{StaticResource PhoneFontSizeNormal}"
Foreground="{StaticResource PhoneForegroundBrush}"
SupportedOrientations="PortraitOrLandscape"
>
<Grid x:Name="LayoutRoot" >
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<StackPanel Grid.Row="0" Margin="12,17,0,28">
<TextBlock Text="WINDOWS PHONE" Style="{StaticResource PhoneTextNormalStyle}"/>
<TextBlock Text="developer's ?" Margin="9,-7,0,0" Style="{StaticResource PhoneTextTitle1Style}"/>
</StackPanel>
<Grid Grid.Row="1" Margin="12,0,12,0"></Grid>
<TextBox Grid.Row="2" LostFocus="TextBoxLostFocus"/>
</Grid>
</phone:PhoneApplicationPage>
public partial class MainPage : PhoneApplicationPage
{
private const double LandscapeShift = -259d;
private const double LandscapeShiftWithBar = -328d;
private const double Epsilon = 0.00000001d;
private const double PortraitShift = -339d;
private const double PortraitShiftWithBar = -408d;
public static readonly DependencyProperty TranslateYProperty = DependencyProperty.Register("TranslateY", typeof(double), typeof(MainPage), new PropertyMetadata(0d, OnRenderXPropertyChanged));
public MainPage()
{
InitializeComponent();
Loaded += MainPageLoaded;
}
public double TranslateY
{
get { return (double)GetValue(TranslateYProperty); }
set { SetValue(TranslateYProperty, value); }
}
private static void OnRenderXPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
((MainPage)d).UpdateTopMargin((double)e.NewValue);
}
private void MainPageLoaded(object sender, RoutedEventArgs e)
{
BindToKeyboardFocus();
}
private void BindToKeyboardFocus()
{
PhoneApplicationFrame frame = Application.Current.RootVisual as PhoneApplicationFrame;
if (frame != null)
{
var group = frame.RenderTransform as TransformGroup;
if (group != null)
{
var translate = group.Children[0] as TranslateTransform;
var translateYBinding = new Binding("Y");
translateYBinding.Source = translate;
SetBinding(TranslateYProperty, translateYBinding);
}
}
}
private void UpdateTopMargin(double translateY)
{
if (IsClose(translateY, LandscapeShift) || IsClose(translateY, PortraitShift)
||IsClose(translateY, LandscapeShiftWithBar) || IsClose(translateY, PortraitShiftWithBar)
)
{
LayoutRoot.Margin = new Thickness(0, -translateY, 0, 0);
}
}
private bool IsClose(double a, double b)
{
return Math.Abs(a - b) < Epsilon;
}
private void TextBoxLostFocus(object sender, RoutedEventArgs e)
{
LayoutRoot.Margin = new Thickness();
}
}
Good Luck....
I have two radio buttons working as radioButton List in UI using MVVM. When the user control is loaded first time, one of the radio button is selected and the related controls are shown in UI... Now when I change the radio button, UI is not getting updated.
Below is the sample XAML:
<Label Grid.Column="0" Grid.Row="3" Content="Exchange Details:" Margin="3" VerticalContentAlignment="Center" Style="{StaticResource NormalLabelStyle}"></Label>
<Grid Grid.Column="1" Grid.Row="3" Width="200">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="20"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<RadioButton GroupName="rdoExchange" Content="Basic" IsChecked="{Binding Path=ExchangeDetailsBasic}" Grid.Column="0" VerticalContentAlignment="Center" VerticalAlignment="Center"></RadioButton>
<RadioButton GroupName="rdoExchange" Content="Advanced" IsChecked="{Binding Path=ExchangeDetailsAdvanced}" Grid.Column="2" VerticalContentAlignment="Center" VerticalAlignment="Center"></RadioButton
</Grid>
<Label Grid.Column="3" Grid.Row="0" Content="Number of Mailbox Profiles:" VerticalContentAlignment="Center" Style="{StaticResource NormalLabelStyle}" Visibility="{Binding Path=IsAdvanced}" ></Label>
<telerik:RadNumericUpDown Grid.Column="4" Grid.Row="0" Margin="3" Value="{Binding Path=NumberofMailboxProfiles}" IsInteger="True" Minimum="1" Maximum="4" HorizontalAlignment="Left" Visibility="{Binding Path=IsAdvanced}">< /telerik:RadNumericUpDown>
Below is my ViewModel code:
private enum ExchangeDetails{
Basic,
Advanced
}
private bool isBasicMode = true;
public bool ExchangeDetailsBasic {
get {
return this.isBasicMode;
}
set {
if (value) {
this.applicationSpecificRequirements[ExchangeDetailsKey] = ExchangeDetails.Basic.ToString();
if (!this.isBasicMode) {
this.CheckBasicOrAdvancedSelecteAndDisplayView();
}
}
}
}
public bool ExchangeDetailsAdvanced {
get {
return !this.isBasicMode;
}
set {
if (value) {
this.applicationSpecificRequirements[ExchangeDetailsKey] = ExchangeDetails.Advanced.ToString();
this.CheckBasicOrAdvancedSelecteAndDisplayView();
}
}
}
public Visibility IsAdvanced { get; private set; }
private void CheckBasicOrAdvancedSelecteAndDisplayView() {
this.isBasicMode = this.applicationSpecificRequirements.ContainsKey(ExchangeDetailsKey) ? (this.applicationSpecificRequirements[ExchangeDetailsKey].Equals(ExchangeDetails.Basic.ToString()) ? true : false) : true;
this.IsAdvanced = this.isBasicMode ? Visibility.Collapsed : Visibility.Visible;
}
Radio buttons, groups, and binding don't mix. This is, amazingly, by design.
There are three ways to change the value of a bound control in the UI. One is that the user can do it himself with a mouse click or keypress. The second is that code can change the value of the data source, and binding will update the value in the UI.
The third way is to set the value explicitly in code. If you do this, the binding on the control you've just set is disabled.
This is a little counter-intuitive. You'd expect the new value to get pushed to the data source. The design assumption is that if you wanted the value to get changed in the data source, you'd change it in the data source, and that your code is manipulating the UI because you don't want it to be bound anymore. This gives you a simple way of manually overriding binding - just set the value of the control in code - that doesn't compel you to find the Binding object and manipulate it explicitly. This makes a certain amount of sense. I guess.
But it creates problems with radio buttons. Because grouped radio buttons change each others' values in code. If you have three radio buttons in a group, and one gets checked, the radio button finds the other buttons in the group and unchecks them. You can see this if you look at the code in Reflector.
So what happens is exactly what you're observing: you click on radio buttons and binding gets disabled.
Here's what you do about it - and this actually makes a considerable amount of sense. Don't use groups. You can use radio buttons, but only for their visual style. Disregard their grouping functionality.
Instead, implement the logic that makes the bound boolean properties mutually exclusive in your view model, e.g.:
public bool Option1
{
set
{
_Option1 = value;
if (value)
{
Option2 = false;
Option3 = false;
}
OnPropertyChanged("Option1");
}
}
If you think about it, this logic really shouldn't be in the view anyway. Because it's logic, and that's what the view model is for. So while it's something of a pain, you can console yourself with the thought that architecturally it's the right thing to do.
I guess you are missing the implementation of INotifyPropertyChanged for the view model class. If you have used two way data binding and you are raising the property changed event when the selection changes everything should work fine. #Zamboni has explained it with the code example.
If you implement INotifyPropertyChanged in your view model and you set Binding Mode=TwoWay in your XAML, you can let the binding take care of the rest for you.
Here is sample using some of your code:
<Grid >
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition/>
</Grid.RowDefinitions>
<RadioButton GroupName="rdoExchange" Content="Basic"
IsChecked="{Binding Path=ExchangeDetailsBasic, Mode=TwoWay}"
Grid.Column="0"
VerticalContentAlignment="Center"
VerticalAlignment="Center"/>
<RadioButton GroupName="rdoExchange" Content="Advanced"
IsChecked="{Binding Path=ExchangeDetailsAdvanced, Mode=TwoWay}"
Grid.Column="1"
VerticalContentAlignment="Center"
VerticalAlignment="Center"/>
<Label Grid.Column="0" Grid.Row="1" Grid.RowSpan="2"
Content="Number of Mailbox Profiles:"
VerticalContentAlignment="Center"
Visibility="{Binding Path=IsAdvanced, Mode=TwoWay}" />
</Grid>
Here is the ViewModel:
public class MainViewModel : ViewModelBase
{
public MainViewModel()
{
}
private bool _isBasicMode = true;
public bool ExchangeDetailsBasic
{
get
{
return this._isBasicMode;
}
set
{
this._isBasicMode = value;
if (value)
{
ExchangeDetailsAdvanced = false;
IsAdvanced = Visibility.Collapsed;
}
this.OnPropertyChanged("ExchangeDetailsBasic");
}
}
private bool _isAdvancedMode = false;
public bool ExchangeDetailsAdvanced
{
get
{
return this._isAdvancedMode;
}
set
{
_isAdvancedMode = value;
if (value)
{
ExchangeDetailsBasic = false;
IsAdvanced = Visibility.Visible;
}
this.OnPropertyChanged("ExchangeDetailsAdvanced");
}
}
private Visibility _isAdvanced = Visibility.Collapsed;
public Visibility IsAdvanced
{
get
{
return _isAdvanced;
}
set
{
_isAdvanced = value;
this.OnPropertyChanged("IsAdvanced");
}
}
}
Here is the base class that implements INotifyPropertyChanged.
public abstract class ViewModelBase : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
}
Robert Rossney's answer is great, but I still think that radio buttons should behave like radio buttons and let the VM handle more important logic.
Here is my solution: an attached property that toggles the IsChecked property of all buttons in the same group. Works on my machine :-)
using System;
using System.Collections.Generic;
using System.Windows;
using System.Windows.Controls;
namespace Elca.MvvmHelpers {
public class RadioButtonHelper : DependencyObject {
private static readonly Dictionary<string, List<RadioButton>> s_group2ButtonsMap = new Dictionary<string, List<RadioButton>>();
private static readonly List<RadioButton> s_knownButtons = new List<RadioButton>();
private static void OnRadioButtonChecked(object sender, RoutedEventArgs e) {
RadioButton rb = (RadioButton)sender;
UncheckOtherButtonsInGroup(rb);
}
public static bool? GetIsChecked(RadioButton d) {
return (bool?) d.GetValue(IsCheckedProperty);
}
public static void SetIsChecked(RadioButton d, bool? value) {
d.SetValue(IsCheckedProperty, value);
}
public static readonly DependencyProperty IsCheckedProperty =
DependencyProperty.RegisterAttached("IsChecked",
typeof(bool?),
typeof(RadioButtonHelper),
new FrameworkPropertyMetadata(false, FrameworkPropertyMetadataOptions.Journal |
FrameworkPropertyMetadataOptions.BindsTwoWayByDefault,
IsCheckedChanged));
public static void IsCheckedChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) {
var rb = d as RadioButton;
if (rb == null) {
throw new Exception("IsChecked attached property only works on a FrameworkElement type");
}
RememberRadioButton(rb);
if ((bool) e.NewValue) {
rb.IsChecked = true; // this triggers OnRadioButtonChecked => other buttons in the same group will be unchecked
}
}
private static void RememberRadioButton(RadioButton rb) {
var groupName = GetGroupName(rb);
// if this button is unknown, add it to the right list, based on its group name
if (s_knownButtons.Contains(rb)) {
return;
}
s_knownButtons.Add(rb);
List<RadioButton> existingButtons;
if (! s_group2ButtonsMap.TryGetValue(groupName, out existingButtons)) {
// unknown group
s_group2ButtonsMap[groupName] = new List<RadioButton> {rb};
RegisterButtonEvents(rb);
} else {
if (! existingButtons.Contains(rb)) {
existingButtons.Add(rb);
RegisterButtonEvents(rb);
}
}
}
private static void RegisterButtonEvents(RadioButton rb) {
rb.Unloaded += OnButtonUnloaded;
rb.Checked += OnRadioButtonChecked;
}
private static void OnButtonUnloaded(object sender, RoutedEventArgs e) {
RadioButton rb = (RadioButton) sender;
ForgetRadioButton(rb);
}
private static void ForgetRadioButton(RadioButton rb) {
List<RadioButton> existingButtons = s_group2ButtonsMap[GetGroupName(rb)];
existingButtons.Remove(rb);
s_knownButtons.Remove(rb);
UnregisterButtonEvents(rb);
}
private static void UnregisterButtonEvents(RadioButton rb) {
rb.Unloaded -= OnButtonUnloaded;
rb.Checked -= OnRadioButtonChecked;
}
private static void UncheckOtherButtonsInGroup(RadioButton rb) {
List<RadioButton> existingButtons = s_group2ButtonsMap[GetGroupName(rb)];
foreach (RadioButton other in existingButtons) {
if (other != rb) {
SetIsChecked(other, false);
}
}
SetIsChecked(rb, true);
}
private static string GetGroupName(RadioButton elt) {
string groupName = elt.GroupName;
if (String.IsNullOrEmpty(groupName)) {
groupName = "none"; // any value will do
}
return groupName;
}
}
}
In the view, for each button:
<RadioButton MvvmHelpers:RadioButtonHelper.IsChecked="{Binding IsExplicitFileSelected, Mode=TwoWay}">
...
</RadioButton>
The VM has a boolean property for each radio button. One must assign a value to each such property to start the listening process of the attached property.
All buttons without a group name are considered to be part of the same group.
I need to create a Dialog / Prompt including TextBox for user input. My problem is, how to get the text after having confirmed the dialog? Usually I would make a class for this which would save the text in a property. However I want do design the Dialog using XAML. So I would somehow have to extent the XAML Code to save the content of the TextBox in a property - but I guess that's not possible with pure XAML. What would be the best way to realize what I'd like to do? How to build a dialog which can be defined from XAML but can still somehow return the input? Thanks for any hint!
The "responsible" answer would be for me to suggest building a ViewModel for the dialog and use two-way databinding on the TextBox so that the ViewModel had some "ResponseText" property or what not. This is easy enough to do but probably overkill.
The pragmatic answer would be to just give your text box an x:Name so that it becomes a member and expose the text as a property in your code behind class like so:
<!-- Incredibly simplified XAML -->
<Window x:Class="MyDialog">
<StackPanel>
<TextBlock Text="Enter some text" />
<TextBox x:Name="ResponseTextBox" />
<Button Content="OK" Click="OKButton_Click" />
</StackPanel>
</Window>
Then in your code behind...
partial class MyDialog : Window {
public MyDialog() {
InitializeComponent();
}
public string ResponseText {
get { return ResponseTextBox.Text; }
set { ResponseTextBox.Text = value; }
}
private void OKButton_Click(object sender, System.Windows.RoutedEventArgs e)
{
DialogResult = true;
}
}
Then to use it...
var dialog = new MyDialog();
if (dialog.ShowDialog() == true) {
MessageBox.Show("You said: " + dialog.ResponseText);
}
Edit: Can be installed with nuget https://www.nuget.org/packages/PromptDialog/
I just add a static method to call it like a MessageBox:
<Window xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
x:Class="utils.PromptDialog"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
WindowStartupLocation="CenterScreen"
SizeToContent="WidthAndHeight"
MinWidth="300"
MinHeight="100"
WindowStyle="SingleBorderWindow"
ResizeMode="CanMinimize">
<StackPanel Margin="5">
<TextBlock Name="txtQuestion" Margin="5"/>
<TextBox Name="txtResponse" Margin="5"/>
<PasswordBox Name="txtPasswordResponse" />
<StackPanel Orientation="Horizontal" Margin="5" HorizontalAlignment="Right">
<Button Content="_Ok" IsDefault="True" Margin="5" Name="btnOk" Click="btnOk_Click" />
<Button Content="_Cancel" IsCancel="True" Margin="5" Name="btnCancel" Click="btnCancel_Click" />
</StackPanel>
</StackPanel>
</Window>
And the code behind:
public partial class PromptDialog : Window
{
public enum InputType
{
Text,
Password
}
private InputType _inputType = InputType.Text;
public PromptDialog(string question, string title, string defaultValue = "", InputType inputType = InputType.Text)
{
InitializeComponent();
this.Loaded += new RoutedEventHandler(PromptDialog_Loaded);
txtQuestion.Text = question;
Title = title;
txtResponse.Text = defaultValue;
_inputType = inputType;
if (_inputType == InputType.Password)
txtResponse.Visibility = Visibility.Collapsed;
else
txtPasswordResponse.Visibility = Visibility.Collapsed;
}
void PromptDialog_Loaded(object sender, RoutedEventArgs e)
{
if (_inputType == InputType.Password)
txtPasswordResponse.Focus();
else
txtResponse.Focus();
}
public static string Prompt(string question, string title, string defaultValue = "", InputType inputType = InputType.Text)
{
PromptDialog inst = new PromptDialog(question, title, defaultValue, inputType);
inst.ShowDialog();
if (inst.DialogResult == true)
return inst.ResponseText;
return null;
}
public string ResponseText
{
get
{
if (_inputType == InputType.Password)
return txtPasswordResponse.Password;
else
return txtResponse.Text;
}
}
private void btnOk_Click(object sender, RoutedEventArgs e)
{
DialogResult = true;
Close();
}
private void btnCancel_Click(object sender, RoutedEventArgs e)
{
Close();
}
}
So you can call it like:
string repeatPassword = PromptDialog.Prompt("Repeat password", "Password confirm", inputType: PromptDialog.InputType.Password);
Great answer of Josh, all credit to him, I slightly modified it to this however:
MyDialog Xaml
<StackPanel Margin="5,5,5,5">
<TextBlock Name="TitleTextBox" Margin="0,0,0,10" />
<TextBox Name="InputTextBox" Padding="3,3,3,3" />
<Grid Margin="0,10,0,0">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Button Name="BtnOk" Content="OK" Grid.Column="0" Margin="0,0,5,0" Padding="8" Click="BtnOk_Click" />
<Button Name="BtnCancel" Content="Cancel" Grid.Column="1" Margin="5,0,0,0" Padding="8" Click="BtnCancel_Click" />
</Grid>
</StackPanel>
MyDialog Code Behind
public MyDialog()
{
InitializeComponent();
}
public MyDialog(string title,string input)
{
InitializeComponent();
TitleText = title;
InputText = input;
}
public string TitleText
{
get { return TitleTextBox.Text; }
set { TitleTextBox.Text = value; }
}
public string InputText
{
get { return InputTextBox.Text; }
set { InputTextBox.Text = value; }
}
public bool Canceled { get; set; }
private void BtnCancel_Click(object sender, System.Windows.RoutedEventArgs e)
{
Canceled = true;
Close();
}
private void BtnOk_Click(object sender, System.Windows.RoutedEventArgs e)
{
Canceled = false;
Close();
}
And call it somewhere else
var dialog = new MyDialog("test", "hello");
dialog.Show();
dialog.Closing += (sender,e) =>
{
var d = sender as MyDialog;
if(!d.Canceled)
MessageBox.Show(d.InputText);
}
You don't need ANY of these other fancy answers. Below is a simplistic example that doesn't have all the Margin, Height, Width properties set in the XAML, but should be enough to show how to get this done at a basic level.
XAML
Build a Window page like you would normally and add your fields to it, say a Label and TextBox control inside a StackPanel:
<StackPanel Orientation="Horizontal">
<Label Name="lblUser" Content="User Name:" />
<TextBox Name="txtUser" />
</StackPanel>
Then create a standard Button for Submission ("OK" or "Submit") and a "Cancel" button if you like:
<StackPanel Orientation="Horizontal">
<Button Name="btnSubmit" Click="btnSubmit_Click" Content="Submit" />
<Button Name="btnCancel" Click="btnCancel_Click" Content="Cancel" />
</StackPanel>
Code-Behind
You'll add the Click event handler functions in the code-behind, but when you go there, first, declare a public variable where you will store your textbox value:
public static string strUserName = String.Empty;
Then, for the event handler functions (right-click the Click function on the button XAML, select "Go To Definition", it will create it for you), you need a check to see if your box is empty. You store it in your variable if it is not, and close your window:
private void btnSubmit_Click(object sender, RoutedEventArgs e)
{
if (!String.IsNullOrEmpty(txtUser.Text))
{
strUserName = txtUser.Text;
this.Close();
}
else
MessageBox.Show("Must provide a user name in the textbox.");
}
Calling It From Another Page
You're thinking, if I close my window with that this.Close() up there, my value is gone, right? NO!! I found this out from another site: http://www.dreamincode.net/forums/topic/359208-wpf-how-to-make-simple-popup-window-for-input/
They had a similar example to this (I cleaned it up a bit) of how to open your Window from another and retrieve the values:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void btnOpenPopup_Click(object sender, RoutedEventArgs e)
{
MyPopupWindow popup = new MyPopupWindow(); // this is the class of your other page
//ShowDialog means you can't focus the parent window, only the popup
popup.ShowDialog(); //execution will block here in this method until the popup closes
string result = popup.strUserName;
UserNameTextBlock.Text = result; // should show what was input on the other page
}
}
Cancel Button
You're thinking, well what about that Cancel button, though? So we just add another public variable back in our pop-up window code-behind:
public static bool cancelled = false;
And let's include our btnCancel_Click event handler, and make one change to btnSubmit_Click:
private void btnCancel_Click(object sender, RoutedEventArgs e)
{
cancelled = true;
strUserName = String.Empty;
this.Close();
}
private void btnSubmit_Click(object sender, RoutedEventArgs e)
{
if (!String.IsNullOrEmpty(txtUser.Text))
{
strUserName = txtUser.Text;
cancelled = false; // <-- I add this in here, just in case
this.Close();
}
else
MessageBox.Show("Must provide a user name in the textbox.");
}
And then we just read that variable in our MainWindow btnOpenPopup_Click event:
private void btnOpenPopup_Click(object sender, RoutedEventArgs e)
{
MyPopupWindow popup = new MyPopupWindow(); // this is the class of your other page
//ShowDialog means you can't focus the parent window, only the popup
popup.ShowDialog(); //execution will block here in this method until the popup closes
// **Here we find out if we cancelled or not**
if (popup.cancelled == true)
return;
else
{
string result = popup.strUserName;
UserNameTextBlock.Text = result; // should show what was input on the other page
}
}
Long response, but I wanted to show how easy this is using public static variables. No DialogResult, no returning values, nothing. Just open the window, store your values with the button events in the pop-up window, then retrieve them afterwards in the main window function.