Close Window without code-behind in WPF - c#

Is it possible to bind a Button to Close the Window without adding a code-behind event?
<Button Content="OK" Command="{Binding CloseWithSomeKindOfTrick}" />
Instead of the following XAML:
<Button Content="OK" Margin="0,8,0,0" Click="Button_Click">
With the code-behind:
private void Button_Click(object sender, RoutedEventArgs e)
{
Close();
}
Thanks!

If you want close the dialog Window, you can add for Button IsCancel property:
<Button Name="CloseButton"
IsCancel="True" ... />
This means the following MSDN:
When you set the IsCancel property of a Button to true, you create a Button that is registered with the AccessKeyManager. The button is then activated when a user presses the ESC key.
Now, if you click on this Button, or press Esc then dialog Window is closing, but it does not work for the normal MainWindow.
To close the MainWindow, you can simply add a Click handler which has already been shown. But if you want a more elegant solution that would satisfy the MVVM style you can add the attached behavior:
public static class ButtonBehavior
{
#region Private Section
private static Window MainWindow = Application.Current.MainWindow;
#endregion
#region IsCloseProperty
public static readonly DependencyProperty IsCloseProperty;
public static void SetIsClose(DependencyObject DepObject, bool value)
{
DepObject.SetValue(IsCloseProperty, value);
}
public static bool GetIsClose(DependencyObject DepObject)
{
return (bool)DepObject.GetValue(IsCloseProperty);
}
static ButtonBehavior()
{
IsCloseProperty = DependencyProperty.RegisterAttached("IsClose",
typeof(bool),
typeof(ButtonBehavior),
new UIPropertyMetadata(false, IsCloseTurn));
}
#endregion
private static void IsCloseTurn(DependencyObject sender, DependencyPropertyChangedEventArgs e)
{
if (e.NewValue is bool && ((bool)e.NewValue) == true)
{
if (MainWindow != null)
MainWindow.PreviewKeyDown += new KeyEventHandler(MainWindow_PreviewKeyDown);
var button = sender as Button;
if (button != null)
button.Click += new RoutedEventHandler(button_Click);
}
}
private static void button_Click(object sender, RoutedEventArgs e)
{
MainWindow.Close();
}
private static void MainWindow_PreviewKeyDown(object sender, KeyEventArgs e)
{
if (e.Key == Key.Escape)
MainWindow.Close();
}
}
And in MainWindow use this Behavior like as:
<Window x:Class="MyProjectNamespace.MainWindow"
xmlns:local="clr-namespace:MyProjectNamespace">
<Button Name="CloseButton"
local:ButtonBehavior.IsClose="True" ... />

Related

How can I change a "Frame Content" in another class? (C# WPF XAML)

once to explain, I open a xaml page via "frame.content" and in this page I have opened, I want to open another one but on the frame where the second page is running.
but i can't open the page,
nothing happens. not even an expection.
So here what I have written:
This is the class from the page that is open
private void bttn_start(object sender, RoutedEventArgs e)
{
MainWindow mw = new MainWindow();
mw.JoinNextPage();
}
This is the MainWindow class where the frame is.
public partial class MainWindow : Window
{
public void JoinNextPage() => pageMirror.Content = new page_finish();
}
You should use RoutedCommand to trigger the Frame navigation instead of the static MainWindow reference.
This removes the complete navigation logic (button event handlers) from your pages.
MainWindow.xaml.cs
public partial class MainWindow : Window
{
public static RoutedCommand NextPageCommand { get; } = new RoutedCommand("NextPageCommand", typeof(MainWindow));
public MainWindow()
{
InitializeComponent();
CommandBindings.Add(
new CommandBinding(NextPageCommand, ExecuteNextPageCommand, CanExecuteNextPageCommand));
}
private void CanExecuteNextPageCommand(object sender, CanExecuteRoutedEventArgs e)
{
e.CanExecute = true;
}
private void ExecuteNextPageCommand(object sender, ExecutedRoutedEventArgs e)
{
// Logic to select the next Frame content
JoinNextPage();
}
}
MainWindow.xaml
<Window>
<Frame>
<Frame.Content>
<Page>
<Button Command="{x:Static local:MainWindow.NextPageCommand}"
Content="Next Page" />
</Page>
</Frame.Content>
</Frame>
</Window>
Try this:
private void bttn_start(object sender, RoutedEventArgs e)
{
MainWindow mw = (MainWindow)Application.Current.MainWindow;
mw.JoinNextPage();
}

How do I assign a global initialized event?

I've got this code in my App.xaml.cs:
protected override void OnStartup(StartupEventArgs e)
{
EventManager.RegisterClassHandler(typeof(TextBox), TextBox.TextChangedEvent, new RoutedEventHandler(TextBox_TextChangedEvent));
}
private void TextBox_TextChangedEvent(object sender, RoutedEventArgs e)
{
// Works
}
I would like to do something similar for the InitializedEvent.
Here's my failed attempt:
protected override void OnStartup(StartupEventArgs e)
{
EventManager.RegisterClassHandler(typeof(FrameworkElement), FrameworkElement.InitializedEvent, new EventHandler(FrameworkElement_InitializedEvent));
}
private void FrameworkElement_InitializedEvent(object sender, EventArgs e)
{
}
Is the InitializedEvent somewhere else?
Is this even possible?
I've tried using the LoadedEvent:
protected override void OnStartup(StartupEventArgs e)
{
EventManager.RegisterClassHandler(typeof(FrameworkElement), FrameworkElement.LoadedEvent, new RoutedEventHandler(FrameworkElement_LoadedEvent));
}
private void FrameworkElement_LoadedEvent(object sender, RoutedEventArgs e)
{
// Fires only for Windows
}
It only fired for Windows and not the controls inside the Windows. I did realize though; that when I added a loaded event to a Label that I had inside my Window; the global FrameworkElement_LoadedEvent fired for that Label even though my normal loaded event (That I made for the Label specifically) was empty. I've also tried these:
EventManager.RegisterClassHandler(typeof(Button), Button.LoadedEvent, new RoutedEventHandler(Button_LoadedEvent));
EventManager.RegisterClassHandler(typeof(Grid), Grid.LoadedEvent, new RoutedEventHandler(Grid_LoadedEvent));
EventManager.RegisterClassHandler(typeof(DataGrid), DataGrid.LoadedEvent, new RoutedEventHandler(DataGrid_LoadedEvent));
But they don't fire unless I add another empty loaded event on those controls specifically.
My goal is to build up a sort of a time log of every control that becomes initialized.
How can I achieve this without adding loaded events on every single control I have?
(I have a lot)
Here you are!
public partial class App : Application
{
// ##############################################################################################################################
// Constructor
// ##############################################################################################################################
#region Constructor
static App()
{
// set MyInitialized=true for new windows (happens before Loaded)
EventManager.RegisterClassHandler(typeof(Window), FrameworkElement.SizeChangedEvent, new RoutedEventHandler(OnSizeChanged));
// our loaded handler
EventManager.RegisterClassHandler(typeof(UIElement), FrameworkElement.LoadedEvent, new RoutedEventHandler(OnLoaded), true);
EventManager.RegisterClassHandler(typeof(ContentElement), FrameworkContentElement.LoadedEvent, new RoutedEventHandler(OnLoaded), true);
}
private static void OnSizeChanged(object sender, RoutedEventArgs e)
{
//Console.WriteLine("SizeChanged {0}", sender);
SetMyInitialized((Window) sender, true);
}
private static void OnLoaded(object sender, RoutedEventArgs e)
{
Trace.WriteLine($"{DateTime.Now:O}: {sender} loaded");
}
#endregion
// ##############################################################################################################################
// MyInitialized
// ##############################################################################################################################
#region MyInitialized
public static void SetMyInitialized(UIElement element, bool value)
{
element.SetValue(MyInitializedProperty, value);
}
public static bool GetMyInitialized(UIElement element)
{
return (bool) element.GetValue(MyInitializedProperty);
}
public static readonly DependencyProperty MyInitializedProperty = DependencyProperty.RegisterAttached("MyInitialized", typeof (bool), typeof (App), new FrameworkPropertyMetadata(false, FrameworkPropertyMetadataOptions.Inherits, OnMyInitializedChanged));
private static void OnMyInitializedChanged(DependencyObject dpo, DependencyPropertyChangedEventArgs ev)
{
if ((bool)ev.NewValue)
{
// registering instance handler unbreaks class handlers
if (dpo is FrameworkElement element)
element.Loaded += _EmptyRoutedEventHandler;
if (dpo is FrameworkContentElement contentElement)
contentElement.Loaded += _EmptyRoutedEventHandler;
} else
{
throw new ArgumentException("Cannot set to false", ev.Property.Name);
}
//Console.WriteLine("MyInitialized {0} {1}=>{2}", dpo, ev.OldValue, ev.NewValue);
}
private static readonly RoutedEventHandler _EmptyRoutedEventHandler = delegate { };
#endregion
}
XAML
<Window x:Class="WpfApp3.MainWindow"
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:WpfApp3"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800"
d:DataContext="{d:DesignInstance local:MainWindow}">
<Grid>
<Border Background="Green" VerticalAlignment="Center" HorizontalAlignment="Center" Width="30" Height="20">
<TextBlock Background="Orange" Text="hello"></TextBlock>
</Border>
</Grid>
</Window>
Sample Console output:
2018-07-31T14:20:52.6052225+02:00: WpfApp3.MainWindow loaded
2018-07-31T14:20:52.6112064+02:00: System.Windows.Controls.Border loaded
2018-07-31T14:20:52.6132008+02:00: System.Windows.Documents.AdornerDecorator loaded
2018-07-31T14:20:52.6141984+02:00: System.Windows.Controls.ContentPresenter loaded
2018-07-31T14:20:52.6141984+02:00: System.Windows.Controls.Grid loaded
2018-07-31T14:20:52.6151966+02:00: System.Windows.Controls.Border loaded
2018-07-31T14:20:52.6161935+02:00: System.Windows.Controls.TextBlock loaded
2018-07-31T14:20:52.6161935+02:00: System.Windows.Documents.AdornerLayer loaded

How to fill ContextMenuStrip in XAML?

I've created WPF application with NotifyIcon to work in tray.
public partial class MainWindow : Window
{
public NotifyIcon NotifyIcon { get; } = new NotifyIcon
{
Icon = Properties.Resources.status_on_ico,
Visible = true
};
public MainWindow()
{
InitializeComponent();
NotifyIcon.ContextMenuStrip = MyContextMenuStrip;
NotifyIcon.Click += NotifyIcon_Click;
}
private void Window_Closing(object sender, CancelEventArgs e)
{
e.Cancel = true;
WindowState = WindowState.Minimized;
}
private void NotifyIcon_Click(object sender, EventArgs e)
{
Show();
}
}
XAML of main window is nothing special and not relevant.
I want to create ContextMenuStrip of NotifyIcon in XAML (I know how to do it code behind but don't want it).
Here is what I've managed.
<WindowsFormsHost>
<wf:ContextMenuStrip x:Name="MyContextMenuStrip" TopLevel="False">
<wf:ContextMenuStrip.Items>
<!-- How to add items here? -->
</wf:ContextMenuStrip.Items>
</wf:ContextMenuStrip>
</WindowsFormsHost>
The question is how to add items to ContextMenuStrip.Items with Name and Click event handlers in XAML?
The question is how to add items to ContextMenuStrip.Items with Name and Click event handlers in XAML?
Try this:
<WindowsFormsHost>
<wf:ContextMenuStrip x:Name="MyContextMenuStrip" TopLevel="False">
<wf:ContextMenuStrip.Items>
<wf:ToolStripMenuItem Text="test1" Click="It_Click" />
<wf:ToolStripMenuItem Text="test2" />
</wf:ContextMenuStrip.Items>
</wf:ContextMenuStrip>
</WindowsFormsHost>
private void It_Click(object sender, EventArgs e)
{
MessageBox.Show("click!");
}

How can I raise a custom Routed Event from user control?

In my user control I have a button that, when clicked, would raise a custom Routed Event. I've attempted to raise it, but it doesn't get fired in the MainWindow.xaml.
Xaml for the button in UserControl:
<Button x:Name="PART_Add" Content="+" Grid.Column="3" Margin="0,0,0,0" Style="{DynamicResource dTranspButton}" Click="btnAdd_Click"/>
UserControl C# code:
//AddClick Event
public static readonly RoutedEvent AddClickEvent = EventManager.RegisterRoutedEvent("AddClick", RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(dCB_Props));
public event RoutedEventHandler AddClick
{
add { AddHandler(AddClickEvent, value); }
remove { RemoveHandler(AddClickEvent, value); }
}
void RaiseAddClickEvent()
{
RoutedEventArgs newEventArgs = new RoutedEventArgs(dCB_Props.AddClickEvent);
}
protected void OnAddClick()
{
RaiseAddClickEvent();
}
//objects events
private void btnAdd_Click(object sender, System.Windows.RoutedEventArgs e)
{
RaiseAddClickEvent();
}
Xaml Code for the UserControl Instance in MainWindow.xaml:
<local:dCB_Props x:Name="cb1" Margin="41.166,0,36.19,25" VerticalAlignment="Bottom" Height="30" Width="141" AddClick="dCB_Props_AddClick">
<local:dCB_Props.Items>
<ComboBoxItem Content="item1"/>
</local:dCB_Props.Items>
</local:dCB_Props>
C# Code that should get fired in MainWindow.xaml.cs:
private void dCB_Props_AddClick(object sender, System.Windows.RoutedEventArgs e)
{
MessageBox.Show("This Works");
}
You need to call
RaiseEvent(new RoutedEventArgs(AddClickEvent));

"UpdateSourceTrigger=PropertyChanged" equivalent for a Windows Phone 7 TextBox

Is there a way to get a TextBox in Windows Phone 7 to update the Binding as the user types each letter rather than after losing focus?
Like the following WPF TextBox would do:
<TextBox Text="{Binding Path=TextProperty, UpdateSourceTrigger=PropertyChanged}"/>
Silverlight for WP7 does not support the syntax you've listed. Do the following instead:
<TextBox TextChanged="OnTextBoxTextChanged"
Text="{Binding MyText, Mode=TwoWay,
UpdateSourceTrigger=Explicit}" />
UpdateSourceTrigger = Explicit is a smart bonus here. What is it? Explicit: Updates the binding source only when you call the UpdateSource method. It saves you one extra binding set when the user leaves the TextBox.
In C#:
private void OnTextBoxTextChanged( object sender, TextChangedEventArgs e )
{
TextBox textBox = sender as TextBox;
// Update the binding source
BindingExpression bindingExpr = textBox.GetBindingExpression( TextBox.TextProperty );
bindingExpr.UpdateSource();
}
I like using an attached property. Just in case you're into those little buggers.
<toolkit:DataField Label="Name">
<TextBox Text="{Binding Product.Name, Mode=TwoWay}" c:BindingUtility.UpdateSourceOnChange="True"/>
</toolkit:DataField>
And then the backing code.
public class BindingUtility
{
public static bool GetUpdateSourceOnChange(DependencyObject d)
{
return (bool)d.GetValue(UpdateSourceOnChangeProperty);
}
public static void SetUpdateSourceOnChange(DependencyObject d, bool value)
{
d.SetValue(UpdateSourceOnChangeProperty, value);
}
// Using a DependencyProperty as the backing store for …
public static readonly DependencyProperty
UpdateSourceOnChangeProperty =
DependencyProperty.RegisterAttached(
"UpdateSourceOnChange",
typeof(bool),
typeof(BindingUtility),
new PropertyMetadata(false, OnPropertyChanged));
private static void OnPropertyChanged (DependencyObject d,
DependencyPropertyChangedEventArgs e)
{
var textBox = d as TextBox;
if (textBox == null)
return;
if ((bool)e.NewValue)
{
textBox.TextChanged += OnTextChanged;
}
else
{
textBox.TextChanged -= OnTextChanged;
}
}
static void OnTextChanged(object s, TextChangedEventArgs e)
{
var textBox = s as TextBox;
if (textBox == null)
return;
var bindingExpression = textBox.GetBindingExpression(TextBox.TextProperty);
if (bindingExpression != null)
{
bindingExpression.UpdateSource();
}
}
}
Not through binding syntax, no, but it's easy enough without. You have to handle the TextChanged event and call UpdateSource on the binding.
private void TextBox_TextChanged(object sender, TextChangedEventArgs e)
{
((TextBox) sender).GetBindingExpression( TextBox.TextProperty ).UpdateSource();
}
This can be converted into an attached behavior as well pretty easily.
In TextChanged event call UpdateSource().
BindingExpression be = itemNameTextBox.GetBindingExpression(TextBox.TextProperty);
be.UpdateSource();
You can write your own TextBox Behavior to handle Update on TextChanged:
This is my sample to PasswordBox but you can simple change it to handle any property of the any object.
public class UpdateSourceOnPasswordChangedBehavior
: Behavior<PasswordBox>
{
protected override void OnAttached()
{
base.OnAttached();
AssociatedObject.PasswordChanged += OnPasswordChanged;
}
protected override void OnDetaching()
{
base.OnDetaching();
AssociatedObject.PasswordChanged -= OnPasswordChanged;
}
private void OnPasswordChanged(object sender, RoutedEventArgs e)
{
AssociatedObject.GetBindingExpression(PasswordBox.PasswordProperty).UpdateSource();
}
}
Ussage:
<PasswordBox x:Name="Password" Password="{Binding Password, Mode=TwoWay}" >
<i:Interaction.Behaviors>
<common:UpdateSourceOnPasswordChangedBehavior/>
</i:Interaction.Behaviors>
</PasswordBox>
UpdateSourceTrigger=Explicit doesnt work for me, hence Im using custom class derivated from TextBox
public class TextBoxEx : TextBox
{
public TextBoxEx()
{
TextChanged += (sender, args) =>
{
var bindingExpression = GetBindingExpression(TextProperty);
if (bindingExpression != null)
{
bindingExpression.UpdateSource();
}
};
}
}
It's just one line of code!
(sender as TextBox).GetBindingExpression(TextBox.TextProperty).UpdateSource();
You can create a generic TextChanged event (for example "ImmediateTextBox_TextChanged") in the code behind of your page, and than link it to any TextBox in the page.
I took Praetorian's answer and made an extension class that inherits TextBox so you don't have to muddle up your view's code behind with this behavior.
C-Sharp:
public class TextBoxUpdate : TextBox
{
public TextBoxUpdate()
{
TextChanged += OnTextBoxTextChanged;
}
private void OnTextBoxTextChanged(object sender, TextChangedEventArgs e)
{
TextBox senderText = (TextBox)sender;
BindingExpression bindingExp = senderText.GetBindingExpression(TextBox.TextProperty);
bindingExp.UpdateSource();
}
}
VisualBasic:
Public Class TextBoxUpdate : Inherits TextBox
Private Sub OnTextBoxTextChanged(sender As Object, e As TextChangedEventArgs) Handles Me.TextChanged
Dim senderText As TextBox = DirectCast(sender, TextBox)
Dim bindingExp As BindingExpression = senderText.GetBindingExpression(TextBox.TextProperty)
bindingExp.UpdateSource()
End Sub
End Class
Then call like this in XAML:
<local:TextBoxUpdate Text="{Binding PersonName, Mode=TwoWay}"/>

Categories

Resources