Custom Content Dialog in UWP with 3+ buttons - c#

I'd like to display a content dialog box that has more than the traditional Primary and Secondary results. Since I can't override the ContentDialogResult enum and add options to that property, it seems my only choice may be to create my own custom control that works similarly to a ContentDialog.
For additional context: Often, one might see a Dialog box show up during a computer/app operation, when the action is redundant, i.e. copying files to a folder, the computer generally offers a dialog box with not 2 options, but 4. -> "Yes to All", "No to All", "Yes", "No". I can't seem to find any cookie cutter ways to take advantage of this seemingly common practice.
I'd like to use it just the same as a normal Content Dialog like so:
var dialog = new MyCustomContentDialog();
var result = dialog.ShowAsync();
and then return an enum just as the normal ContentDialog but instead have it return 1 of 4 options, not just 2.
Any help or recommendations would be great. Thanks.

I'd like to display a content dialog box that has more than the traditional Primary and Secondary results.
The ContentDialog has 2 built-in buttons(the primary/secondary button) that let a user respond to the dialog. If you want more buttons to let the user to respond to the dialog, you should be able to achieve this by including these buttons in the content of the dialog.
Following is a simple sample shows how to create and use a custom dialog with 3 button:
MyCustomContentDialog.xaml
<ContentDialog
x:Class="ContentDialogDemo01.MyCustomContentDialog"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:ContentDialogDemo01"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
x:Name="dialog"
Title="Delete">
<!-- Content body -->
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}" Margin="0,20">
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition Height="200" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<TextBlock Grid.ColumnSpan="3" Text="Delete file A?" Margin="5" />
<Button Grid.Row="1" Content="Yes" x:Name="btn1" Click="btn1_Click" Margin="5,0" Width="100" />
<Button Grid.Row="1" Grid.Column="1" Content="No" x:Name="btn2" Click="btn2_Click" Margin="5,0" Width="100" />
<Button Grid.Row="1" Grid.Column="2" Content="Cancle" x:Name="btn3" Click="btn3_Click" Margin="5,0" Width="100" />
</Grid>
</ContentDialog>
MyCustomContentDialog.xaml.cs
namespace ContentDialogDemo01
{
// Define your own ContentDialogResult enum
public enum MyResult
{
Yes,
No,
Cancle,
Nothing
}
public sealed partial class MyCustomContentDialog : ContentDialog
{
public MyResult Result { get; set; }
public MyCustomContentDialog()
{
this.InitializeComponent();
this.Result = MyResult.Nothing;
}
// Handle the button clicks from dialog
private void btn1_Click(object sender, RoutedEventArgs e)
{
this.Result = MyResult.Yes;
// Close the dialog
dialog.Hide();
}
private void btn2_Click(object sender, RoutedEventArgs e)
{
this.Result = MyResult.No;
// Close the dialog
dialog.Hide();
}
private void btn3_Click(object sender, RoutedEventArgs e)
{
this.Result = MyResult.Cancle;
// Close the dialog
dialog.Hide();
}
}
}
Here is the code to show the custom dialog and use returned custom result:
private async void ShowDialog_Click(object sender, RoutedEventArgs e)
{
// Show the custom dialog
MyCustomContentDialog dialog = new MyCustomContentDialog();
await dialog.ShowAsync();
// Use the returned custom result
if (dialog.Result == MyResult.Yes)
{
DialogResult.Text = "Dialog result Yes.";
}
else if (dialog.Result == MyResult.Cancle)
{
DialogResult.Text = "Dialog result Canceled.";
}
else if (dialog.Result == MyResult.No)
{
DialogResult.Text = "Dialog result NO.";
}
}
Here is the entire sample. Following is the output:

Just for completeness - the ContentDialog class by default actually offers three buttons - Primary, Secondary and Close. Close is what is triggered when the user presses escape, but if you set CloseButtonText the button will show up as the third button in the dialog's footer.

Related

Remove Button Text If There Isn't Enough Space

I'm working with an item template that should display one or more buttons per item. There are three buttons that I want to be displayed on a horizontal line. Each button has an icon and some text.
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Button x:Name="LeftButton"
Text="Left Button"
ImageSource="left.png"
HorizontalOptions="Start"/>
<Button x:Name="CenterButton"
Text="Center Button"
ImageSource="center.png"
HorizontalOptions="Center"/>
<Button x:Name="RightButton"
Text="Right Button"
ImageSource="right.png"
HorizontalOptions="End"/>
</Grid>
This is working good so far. The three buttons display the icon and text and they are aligned left, center and right respectively.
Now, I want the buttons to only display the icons, if the container is not wide enough to display all the text. As far as I understand MVVM, this should be the responsibility of the View.
My intended solution would be something like this:
public partial class ItemTemplate : ContentView
// ContentView is our implementation of a UI element.
{
public ItempTemplate()
{
InitializeComponent();
SizeChanged += HandleSizeChanged;
}
private void HandleSizeChanged(object sender, EventArgs e)
{
if (/* not enough space */)
{
LeftButton.Text = string.Empty;
CenterButton.Text = string.Empty;
RightButton.Text = string.Empty;
}
else
{
LeftButton.Text = "Left Button";
CenterButton.Text = "Center Button";
RightButton.Text = "Right Button";
}
}
}
Is there any way to know if the container is wide enough for all three button? If possible, I'd like to use a dynamic solution, because the button text will eventually be translated.
You can place the code into a custom contentview , and decide to hide/show the text in the event LayoutChanged according to the container's width .
Custom View xaml
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Button x:Name="LeftButton" Grid.Column="0" Text="Left Button" ImageSource="dots.png" HorizontalOptions="Start"/>
<Button x:Name="CenterButton" Grid.Column="1" Text="Center Button" ImageSource="dots.png" HorizontalOptions="Center"/>
<Button x:Name="RightButton" Grid.Column="2" Text="Right Button" ImageSource="dots.png" HorizontalOptions="End"/>
</Grid>
Custom View code behind
public partial class MyView : ContentView
{
public MyView()
{
InitializeComponent();
this.LayoutChanged += MyView_LayoutChanged;
}
private void MyView_LayoutChanged(object sender, EventArgs e)
{
var view = sender as View;
if (view.Width < 200)
{
LeftButton.Text = string.Empty;
CenterButton.Text = string.Empty;
RightButton.Text = string.Empty;
}
else
{
LeftButton.Text = "Left Button";
CenterButton.Text = "Center Button";
RightButton.Text = "Right Button";
}
}
}
Scenario : container is large enough.
xmlns:local="clr-namespace:FormsApp"
<local:MyView/>
Scenario : container is small ,wrapped inside another layout .
<Grid HorizontalOptions="Start" WidthRequest="199" >
<local:MyView/>
</Grid>
I solved my problem by using a custom button implementation (mostly because I needed some other additional features).
The custom button contains an Image and a Label. In addition to the standard button features I need, I added these methods to the code-behind:
public void ExpandText() {
Label.IsVisible = true;
}
public void CollapseText() {
Label.IsVisible = false;
}
public bool IsTextCollapsed() {
return !Label.IsVisible;
}
public double GetWidthAsExpanded() {
return Image.Width + Label.Width;
}
In the container's code-behind I check if the button's width fits within its container and collapse/expand accordingly. For that to work, I added containers for each button.
public ItemTemplate() {
InitializeComponent();
LayoutChanged += HandleLayoutChanged;
}
private static void HandleLayoutChanged(object sender, EventArgs e)
{
if (!(sender is ItemTemplate itemTemplate))
{
return;
}
if (itemTemplate.ButtonContainerLeft.Width > itemTemplate.ButtonLeft.GetWidthAsExpanded()
&& itemTemplate.ButtonContainerCenter.Width > itemTemplate.ButtonCenter.GetWidthAsExpanded()
&& itemTemplate.ButtonContainerRight.Width > itemTemplate.ButtonRight.GetWidthAsExpanded())
{
ExpandAllButtons(itemTemplate);
}
else
{
CollapseAllButtons(itemTemplate);
}
}
Because I added ExpandText and CollapseText earlier, I don't have to "remember" what the text inside the button was, because I just collapse the label within the button. GetWidthAsExpanded will always return the necessary width of the button, even if it is collapsed.
side-note 1: I could've just added the width-check within the custom button implementation, but not every ItemTemplate has all buttons and if any label is collapsed, all labels should be collapsed.
side-note 2: I needed button containers either way, because the left button will either be "Mark as Read" or "Mark as Unread" depending on the state of the ItemTemplate data context. So there are actually two buttons in the first container.

C# WPF - Popup which doesn't get focused

Im simply trying to make a little popup which shows a message in the corner. This Popup shuld be at the top of every other Window which I achieved with "TopMost", but I can't seem to get the unfocusable thing to work...
My PopUp XAML:
<Window x:Class="message.Popup"
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:message"
mc:Ignorable="d"
Title="Popup" Height="129.808" Width="300" Focusable="False" Topmost="True">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="179*"/>
<ColumnDefinition Width="113*"/>
</Grid.ColumnDefinitions>
<Label x:Name="label" Content="" HorizontalAlignment="Left" Margin="10,40,0,0" VerticalAlignment="Top" Width="272" Grid.ColumnSpan="2"/>
</Grid>
How I call it (from annother Window):
private void textBox_TextChanged(object sender, TextChangedEventArgs e)
{
new Popup(textBox.Text).Show();
}
PopUp code:
public Popup(string text)
{
InitializeComponent();
label.Content = text;
}
you must define your Mainwindow as Owner of the Popup and center the StartupLocation Property. Something like this:
PopUpWindow.Owner = MainWindow;
PopUpWindow.WindowStartupLocation = WindowStartupLocation.CenterOwner;
Or if you are calling it from another window:
PopUpWindow.Owner = this;
PopUpWindow.WindowStartupLocation = WindowStartupLocation.CenterOwner;
However I must say: this is not MVVM, since you are storing text in the Window class, and I strongly recommend you start reading about MVVM.

C# Events in a tic tac toe game

I am new in coding and atm trying to understand events what is an annoying stage but super important I guess. I just made a tic tac toe game and it is working but not really "beautiful" coded. I really have problems in using the events. well I am reading 3 different books, google is my best friend and I guess I red all the StackOverflow posts about events but the bulb in my head is never shining :P so I will give you boys a part of my code and I added some comments for the understanding:
/*I have 9 buttons(3x3) which are the play field */
private void Feld1_Click(object sender, RoutedEventArgs e)
{
// in my game each player have 1 Radiobutton so they check a RButton and then it's their turn
if (Player1RButton.IsChecked == true)
{
// i dont wanted to use "X" or "O" so i chose the colors green and yellow
Feld1.Background = Brushes.Green;
// Feld1G is for example that Player1 (green) is "owning" this
// field/button so i can later check who won the game
Feld1G = 1;
Feld1Y = 0;
}
if (Player2RButton.IsChecked == true)
{
//here is the same thing happening like in the example of green
Feld1.Background = Brushes.Yellow;
Feld1Y = 1;
Feld1G = 0;
}
}
private void Feld2_Click(object sender, RoutedEventArgs e)
{
if (Player1RButton.IsChecked == true)
{
Feld2.Background = Brushes.Green;
Feld2G = 1;
Feld2Y = 0;
}
if (Player2RButton.IsChecked == true)
{
Feld2.Background = Brushes.Yellow;
Feld2Y = 1;
Feld2G = 0;
}
}
here is an example how the ui looks like:tic tac toe exampe
now what I would like to do in my words cause I don't know how to code it:
// I have no idea if this is the right start
public void OnClick (EventArgs e)
{
/* now I guess here have to happen something like this, for example, field9 was clicked and radiobutton2 is checked: know that button9 have been clicked know radiobutton is checked and now brush (this.button?) button/field9 and set Feld9Y=1;
}
*/
I want to make it a bit more clearly here: I want all the functions run from the method above and not in each button event for itself
so my questions:
1. what do I have to do to make this work the way i explained above to make 1 method for all of my buttons
and it would be great if you boys could make a good story why I have to use it this way and how it works so a brainless ape like me can understand the event stuff and the bulb will finally shine bright like a diamond :P
Edit:here is the link for my whole code:
https://codereview.stackexchange.com/questions/164462/c-events-in-a-tic-tac-toe-game
Here is a quick example to get you started. I only implemented changing the background and nothing more.
Below is the xaml. I set up a very simple board with 9 regular buttons and 2 radio buttons. I did not implement anything else.
<Grid x:Name="board">
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition />
<ColumnDefinition />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<Button Content="Button"/>
<Button Content="Button"
Grid.Column="1" />
<Button Content="Button"
Grid.Column="2"/>
<Button Content="Button"
Grid.Row="1" />
<Button Content="Button"
Grid.Row="1"
Grid.Column="1" />
<Button Content="Button"
Grid.Row="1"
Grid.Column="2" />
<Button Content="Button"
Grid.Row="2" />
<Button Content="Button"
Grid.Row="2"
Grid.Column="1" />
<Button Content="Button"
Grid.Row="2"
Grid.Column="2" />
<RadioButton x:Name="playerOneRadioButton"
IsChecked="True"
Content="Player 1"
Grid.Column="3"
HorizontalAlignment="Left"
Margin="10,10,0,0"
Grid.Row="1"
VerticalAlignment="Top" />
<RadioButton x:Name="playerTwoRadioButton"
Content="Player 2"
Grid.Column="3"
HorizontalAlignment="Left"
Margin="10,30,0,0"
Grid.Row="1"
VerticalAlignment="Top" />
</Grid>
Below is the code behind.
I hooked up each buttons click event to point to the Button_Click method.
In that method I first cast the sender to type Button. I then change the background color of the button to either yellow or green.
public partial class MainWindow: Window
{
public MainWindow( )
{
InitializeComponent( );
// Iterate through all of the child elements
// contained in the Grid control which I named "board".
foreach( var control in board.Children.OfType<Button>( ) )
{
// Hook up the event handler of each button.
control.Click += Button_Click;
}
}
private void Button_Click( object sender, RoutedEventArgs e )
{
// Safe cast the sender to type Button.
var button = sender as Button;
if( button == null ) return;
// Change the background color of the button
// yellow or green based on which radio button
// is checked.
button.Background = playerOneRadioButton.IsChecked.Value ? Brushes.Green : Brushes.Yellow;
}
}
EDIT:
Question 1: The Grid which I have on the design surface is named "board". Children is a property of the Grid control. All child elements (elements that reside inside of the grid, like the buttons) are added to the Children property of the grid control. The Children property is a collection, or you could say a list, of all child elements that reside in the grid control. I loop through each child control in the grid that is of type button.
Question 2: The ?: is called a ternary operator. It is used for writing a conditional statement (an if statement). You can also write it as follows:
if( playerOneRadioButton.IsChecked.Value )
{
button.Background = Brushes.Green;
}
else
{
button.Background = Brushes.Yellow;
}
Question 3: I'm currently casting the object sender to type button using whats called a "safe cast". In other words, if the cast fails (say sender wasn't a button and was some other control instead) then null is returned. I check for this null condition to ensure that the cast was successful. If the button variable is null then the cast was not successful and I want to exit (return) out of that method. No code below the
if(button == null) return;
will execute.
The single method you want to use should look like this:
private void Feld_Click(object sender, RoutedEventArgs e)
{
var currentButton = (Button)sender;
if (Player1RButton.IsChecked == true)
{
currentButton.Background = Brushes.Green;
}
}
Add this as OnClick handler for all the buttons.
From there you can calculate the Feld color whenever you need it, by accessing the Background property in a separate method.

E.Handled Not Work From TouchEventArgs in ShowDialog

I've got some trouble with stoping tunneling with Touch when a window was launched with ShowDialog().
My problem is : When I Touch the button in my Window, the clic(or touch ) continue to the MainWindow and open a new Window if antoher Button is behind.
I try to use
e.Handle = true;
To stop tunneling, it work if i clic with my mouse, but if i touch my screen it don't.
Here is a sample Of Code : ( This sample Window have just one button 'OK'. )
C#
public partial class MessageWindow : Window
{
.... other code ...
public static MessageBoxResult Show(string caption, MessageTypes type, MessageBoxButton buttons)
{
MessageWindow wnd = new MessageWindow();
wnd.Owner = Application.Current.MainWindow;
wnd.Title = "Error Message";
wnd.IsError = true;
wnd.Message = caption;
wnd.IsOk = true;
wnd.ShowDialog();
return wnd.Result;
}
private void OnOK()
{
Result = MessageBoxResult.OK;
this.DialogResult = true;
}
private void _btOK_Click(object sender, RoutedEventArgs e)
{
e.Handled = true;
OnOK();
}
private void _btOKonly_TouchDown(object sender, TouchEventArgs e)
{
e.Handled = true;
OnOK();
}
.... other code again ....
}
XAML
<Grid x:Name="LayoutRoot" Background="White" Width="640" Height="480">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Border Background="Black" Padding="20,10">
<TextBlock TextWrapping="Wrap" Text="{Binding Title, ElementName=Window}"/>
</Border>
<Button x:Name="_btOKonly" Content="OK" Click="_btOK_Click" TouchDown="_btOK_Click"Grid.Row="2"/>
<StackPanel>
< ... text of error ...>
</StackPanel>
</Grid>
Here is the code to launch my window :
C#
MessageWindow.Show(" This is a sample of error message", MessageWindow.MessageTypes.Error);
Thanks EveryOne Can Help Me :)
Finaly I found my Answer.
In fact i have to just captur "Clic" and not the "Touch" action like this
<Button x:Name="_btOKonly" Content="OK" Click="_btOK_Click" Grid.Row="2"/>
And The "Clic" is Handle like it does.

Saving user color settings of a clicked Button in WPF

I have a little problem with saving some properties of my Buttons. The Buttons are small and with a variety of colors. When i press one button, some specified colors are changing... and i want to save them for the next start up. The textbox values i can save them but this ...i can't.
Code:
public MainWindow()
{
InitializeComponent();
//blueColor.RaiseEvent(new RoutedEventArgs(Button.ClickEvent));
//this.Property = Properties.Settings.Default.userColor;
}
private void blueColor_Click(object sender, RoutedEventArgs e)
{
var bc = new BrushConverter();
Main.Background = (Brush)bc.ConvertFrom("#FF007CE4");
startButton.Foreground = (Brush)bc.ConvertFrom("#FF007CE4");
closeButton.Foreground = (Brush)bc.ConvertFrom("#FF007CE4");
Properties.Settings.Default.userColor = true;
Properties.Settings.Default.Save();
}
private void purpleColor_Click(object sender, RoutedEventArgs e)
{
var bc = new BrushConverter();
Main.Background = (Brush)bc.ConvertFrom("#FF8701B9");
startButton.Foreground = (Brush)bc.ConvertFrom("#FF8701B9");
closeButton.Foreground = (Brush)bc.ConvertFrom("#FF8701B9");
}
I think I need the last clicked Button to be saved because I have allot of colors and maybe the .RaiseEvent can help here.
This is how it looks like:
Those 3 little buttons:
white
blue
red
are for changing the look of the program. At every start, the default is back.
You can store the color as a simple string and TypeConverter automatically converts it to type Brush. Below is an example.
Binding default value from XAML:
xmlns:properties="clr-namespace:WorkWithSettings.Properties"
<Button Width="100" Height="30"
Background="{Binding Source={x:Static properties:Settings.Default}, Path=Setting, Mode=TwoWay}" />
Set value from code:
private void Button_Click(object sender, RoutedEventArgs e)
{
WorkWithSettings.Properties.Settings.Default.Setting = "#FF007CE4";
}
Note: Setting - this is just the type of String.
More information you can see here:
TypeConverters and XAML
Edit:
Below I'll show you an example, that I hope will help you.
So, go into the settings of the project: Project -> Properties -> Parameters. This opens a window of approximately:
Here we have a property ButtonColor, defined in the settings. For example, I took the Button, which changes the background, depending on the color of the pressed button.
In order to property Background the synchronize with settings to do, so:
<Button Width="100" Height="30"
Content="TestButton"
Background="{Binding Source={x:Static properties:Settings.Default}, Path=ButtonColor, Mode=TwoWay}" />
The default background color of white. Now, to set the background color at the button, we change the parameter settings, like this:
private void Blue_Click(object sender, RoutedEventArgs e)
{
WorkWithSettings.Properties.Settings.Default.ButtonColor = "Blue";
}
To save changes to the settings, you need to call a method Save():
private void Save_Click(object sender, RoutedEventArgs e)
{
WorkWithSettings.Properties.Settings.Default.Save();
}
Now, the next time you start the program, the color will be the one that was set last.
Full example
XAML
<Window x:Class="WorkWithSettings.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:properties="clr-namespace:WorkWithSettings.Properties"
WindowStartupLocation="CenterScreen"
Title="MainWindow" Height="350" Width="525">
<Grid>
<TextBlock Width="100" Height="30" Text="{Binding Source={x:Static properties:Settings.Default}, Path=ButtonColor, Mode=TwoWay}" Margin="0,60,0,0" />
<Button Width="100" Height="30" Content="TestButton" Background="{Binding Source={x:Static properties:Settings.Default}, Path=ButtonColor, Mode=TwoWay}" />
<WrapPanel>
<Button Name="Blue" Width="100" Height="30" Content="BlueColor" VerticalAlignment="Top" Click="Blue_Click" />
<Button Name="Red" Width="100" Height="30" Content="RedColor" VerticalAlignment="Top" Click="Red_Click" />
<Button Name="White" Width="100" Height="30" Content="WhiteColor" VerticalAlignment="Top" Click="White_Click" />
</WrapPanel>
<Button Name="Save" Width="60" Height="30" Content="Save" VerticalAlignment="Top" HorizontalAlignment="Right" Click="Save_Click" />
</Grid>
</Window>
Code behind
namespace WorkWithSettings
{
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void White_Click(object sender, RoutedEventArgs e)
{
WorkWithSettings.Properties.Settings.Default.ButtonColor = "White";
}
private void Blue_Click(object sender, RoutedEventArgs e)
{
WorkWithSettings.Properties.Settings.Default.ButtonColor = "Blue";
}
private void Red_Click(object sender, RoutedEventArgs e)
{
WorkWithSettings.Properties.Settings.Default.ButtonColor = "Red";
}
private void Save_Click(object sender, RoutedEventArgs e)
{
WorkWithSettings.Properties.Settings.Default.Save();
}
}
}
Output
You probably need to create items in the Settings tab of your project that store the information about the color. I would recommend storing the hex strings. Then, on MainForm_Load retrieve those values.
Make sure to also put the settings in the User scope, or else they will reset each time they close the application.

Categories