Intention is to get and handle Routed Events from child Window. I cannot (read: do not want to) use direct routing as there are more elements between (a Command).
The following example demonstrates that Event Routing is not working from one Window to second Window.
Child window XAML:
<Window x:Class="WpfApplication1.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Window1" Height="300" Width="300">
<Grid>
<Button Content="Raise Routing Event" HorizontalAlignment="Left" Margin="50" VerticalAlignment="Top"
Width="150" Click="RaiseRoutedEvent" />
</Grid>
Raise Event Code:
using System.Windows;
namespace WpfApplication1
{
public partial class Window1
{
private static readonly RoutedEvent ChildWindowEvent = EventManager.RegisterRoutedEvent("ButtonClicked",
RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(Window1));
public Window1()
{
InitializeComponent();
}
public event RoutedEventHandler ButtonClicked
{
add { AddHandler(ChildWindowEvent, value); }
remove { RemoveHandler(ChildWindowEvent, value); }
}
private void RaiseRoutedEvent(object sender, RoutedEventArgs e)
{
RoutedEventArgs eventArgs = new RoutedEventArgs(ChildWindowEvent);
RaiseEvent(eventArgs);
}
}
}
Main window:
<Window x:Class="WpfApplication1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:wpfApplication1="clr-namespace:WpfApplication1"
Title="MainWindow" Height="350" Width="525" wpfApplication1:Window1.ButtonClicked="HandleRoutedEvent">
<Grid>
<Button Content="Open new window" HorizontalAlignment="Left" Margin="50" VerticalAlignment="Top"
Width="150" Click="OpenNewWindow" />
</Grid>
</Window>
Window which should handle the routed event:
using System.Windows;
namespace WpfApplication1
{
public partial class MainWindow
{
public MainWindow()
{
InitializeComponent();
}
private void OpenNewWindow(object sender, RoutedEventArgs e)
{
Window1 window1 = new Window1();
window1.ShowDialog();
}
private void HandleRoutedEvent(object sender, RoutedEventArgs e)
{
MessageBox.Show("This message is shown from the Main Window");
}
}
}
The event is raised from Window1 but the MainWindow.HandleRoutedEvent does not hit its break point. Why?
Routed Events travel along the visual tree. A top-level window is a visual tree root and is not part of its owner's visual tree. Therefore, any events which bubble up from within a child window will not propagate up to the owner window.
As an aside, I noticed a couple issues in your example code. In your xaml, you register a handler with attached event syntax, but you have declared an instance event. If you want to implement an attached event, you will need these members:
public static readonly RoutedEvent ButtonClickedEvent = EventManager.RegisterCrossWindowRoutedEvent(
"ButtonClicked",
RoutingStrategy.Bubble,
typeof(RoutedEventHandler),
typeof(ChildWindow));
public static void AddButtonClickedHandler(UIElement target, RoutedEventHandler handler)
{
target.AddHandler(ButtonClickedEvent, handler);
}
public static void RemoveButtonClickedHandler(UIElement target, RoutedEventHandler handler)
{
target.RemoveHandler(ButtonClickedEvent, handler);
}
If you intended to have an instance event, the event name should correspond with the name provided when registering the routed event ("ButtonClicked").
Related
I currently have a Button inside my custom UserControl that needs to have a method name binded to it's Click dependency, the method name being provided from a custom dependency property in the user control. Any ideas on how to do this?
Page.xaml
<local:CustomButton OnClick="CustomButton1_Click" ... />
Page.xaml.cs
private void CustomButton1_Click(object sender, RoutedEventArgs e)
{
// do something...
}
CustomButton.xaml
<Button Click={x:Bind OnClick} ... />
CustomButton.xaml.cs
public sealed partial class CustomButton : UserControl
{
...
public static readonly DependencyProperty OnClickProperty = DependencyProperty.Register("OnClick", typeof(string), typeof(CustomButton), new PropertyMetadata(true));
public bool IsNavigator
{
get => (string)GetValue(OnClickProperty);
set => SetValue(OnClickProperty, value);
}
}
Do you mean you want to call CustomButton1_Click when CustomButton is clicked?
CustomButton.xaml
<UserControl
x:Class="UserControls.CustomButton"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:UserControls"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d">
<Grid>
<Button Content="Button" Click="Button_Click"/>
</Grid>
</UserControl>
CustomButton.xaml.cs
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls;
using System;
namespace UserControls;
public sealed partial class CustomButton : UserControl
{
public CustomButton()
{
this.InitializeComponent();
}
public event EventHandler? OnClick;
private void Button_Click(object sender, RoutedEventArgs e)
{
OnClick?.Invoke(this, EventArgs.Empty);
}
}
And use it like this:
<Grid>
<local:CustomButton OnClick="CustomButton_OnClick" />
</Grid>
so i want to outsource some things from MainWindow.xaml to App.xaml like this for example :
<Application x:Class="SVGTesting.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
StartupUri="MainWindow.xaml">
<Application.Resources>
<ResourceDictionary>
<DataTemplate DataType="{x:Type ContentControl}" x:Key="Test1">
<StackPanel Orientation="Horizontal">
<Button Content="Button1" Click="Button_Click" x:Name="Button1"/>
<Button Content="Button2" Click="Button_Click" x:Name="Button2"/>
</StackPanel>
</DataTemplate>
</ResourceDictionary>
</Application.Resources>
</Application>
In MainWindow.xaml then i have something like this
<ContentControl ContentTemplate="{StaticResource Test1}"/>
But now VS says that i cannot use the function "Button_Click" because its not in the codebehind from App.xaml. So how can i call this function from MainWindow in App.xaml?
Is there any way? I don't want answers like MVVM or Command. If it's not possible to solve then WPF is unfortunately useless for me.
Thanks and Greetings.
This is not the easiest thing to do as WPF expect things to be done in its own way. But there's few options, from easiest to hardest.
1. Don't do anything
Easiest way is to keep your data templates inside the MainWindow.xaml.
2. Use Commands instead of event handlers
You currently have event handlers defined like this:
<Button Content="Button1" Click="Button_Click"
"More-WPF way" of doing this would be to replace Click's event handler with a command with this quite cumbersome syntax:
<Button Content="Test" Command="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}}, Path=DataContext.OnClickCommand}"></Button>
And then define the command in your MainWindow:
public ICommand OnButtonClick
{
get
{
return new Command(() =>
{
this.Text.Text = "Updated";
});
}
}
3. Define the event handlers in App.xaml.cs and use that to route the event handlers
I don't recommend this as it get tiresome to keep things synced but it's possible. Create and event handler in App.xaml.cs:
public partial class App : Application
{
private void Button_Click(object sender, RoutedEventArgs e)
{
}
}
Then use the sender to access the MainWindow instance and call it's method:
private void Button_Click(object sender, RoutedEventArgs e)
{
var mainWindow = (MainWindow)Window.GetWindow((DependencyObject)sender);
mainWindow.DoWork();
}
In my second example Command is defined like the following:
public class Command : ICommand
{
public delegate void ICommandOnExecute();
private ICommandOnExecute _execute;
public event EventHandler CanExecuteChanged;
public Command(ICommandOnExecute onExecuteMethod)
{
_execute = onExecuteMethod;
}
public bool CanExecute(object parameter)
{
return true;
}
public void Execute(object parameter)
{
_execute?.Invoke();
}
}
You can't do it. See MSDN documentation for Code-Behind:
The event handlers you write must be instance methods defined by the
partial class within the namespace identified by x:Class. You cannot
qualify the name of an event handler to instruct a XAML processor to
look for that handler in a different class scope. You also cannot use
a static method as an event handler.
In WPF you can use a behaviors instead.
xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
<Button Content="btnWithBehavior">
<i:Interaction.Behaviors>
<local:HandleButtonClick/>
</i:Interaction.Behaviors>
</Button>
public class HandleButtonClick : Behavior<Button>
{
protected override void OnAttached()
{
base.OnAttached();
AssociatedObject.Click += AssociatedObject_Click; ;
}
private void AssociatedObject_Click(object sender, RoutedEventArgs e)
{
//Move your MainWindow.Button_Click here;
}
protected override void OnDetaching()
{
base.OnDetaching();
AssociatedObject.Click -= AssociatedObject_Click;
}
}
I have the following UserControl:
<UserControl x:Class="DueVCheck.Pl.Gui.CustomControl.ListInfoBar"
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:local="clr-namespace:DueVCheck.Pl.Gui.CustomControl"
mc:Ignorable="d" d:DataContext="{d:DesignInstance local:ListInfoBar}" >
<Button Name="NewRowBtn" Margin="5" Click="NewRowBtn_OnClick" Content="New"/>
</UserControl>
The Code-Behind file:
using System.Windows;
namespace DueVCheck.Pl.Gui.CustomControl
{
public partial class ListInfoBar
{
public ListInfoBar()
{
DataContext = this;
InitializeComponent();
}
public static readonly RoutedEvent NewClickEvent =
EventManager.RegisterRoutedEvent("NewClick", RoutingStrategy.Bubble,
typeof(RoutedEventHandler), typeof(ListInfoBar));
public event RoutedEventHandler NewClick
{
add { AddHandler(NewClickEvent, value); }
remove { RemoveHandler(NewClickEvent, value); }
}
private void NewRowBtn_OnClick(object sender, RoutedEventArgs e)
{
RaiseEvent(new RoutedEventArgs(NewClickEvent)); }
}
}
}
Usage of the Usercontrol:
<customControl:ListInfoBar Margin="10,0,10,0" NewClick="NewRowOnClick"/>
What i need is that the Usercontrol disables the NewRowBtn whenever the NewClick is not bound via XAML. The Problem is that Add or Remove get never called when binding the Event over XAML.
How to fix this?
I have a WPF application which shows a window as a DialogBox.
I want to close this DialogBox when I click anywhere on MainWindow in application.
Here I am adding one sample example to explain
MainWindow of application
<Window x:Class="WpfApplication1.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:WpfApplication1"
mc:Ignorable="d"
Title="MainWindow" Height="350" Width="525">
<Grid>
<Button Content="Open new Window" Click="Button_Click" HorizontalAlignment="Center" VerticalAlignment="Center"/>
</Grid>
</Window>
Code behind main Window
namespace WpfApplication1
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void Button_Click(object sender, RoutedEventArgs e)
{
Window1 temp_Window = new Window1();
temp_Window.ShowDialog();
}
}
}
Child window Which I try to close
<Window x:Class="WpfApplication1.Window1"
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:WpfApplication1"
mc:Ignorable="d"
Title="Window1" Height="300" Width="300" Deactivated="Window_Deactivated">
<Grid>
<TextBlock>New Window</TextBlock>
</Grid>
</Window>
Code behind This Child Window
namespace WpfApplication1
{
/// <summary>
/// Interaction logic for Window1.xaml
/// </summary>
public partial class Window1 : Window
{
public Window1()
{
InitializeComponent();
}
private void Window_Deactivated(object sender, EventArgs e)
{
Close();
}
}
}
Thanks
you can do this with Deactivated event of your DialogBox window
something like this
XAML
<Window x:Class="WpfApplication1.Window1"
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:WpfApplication1"
mc:Ignorable="d"
Title="Window1" Height="200" Width="200" WindowStartupLocation="CenterScreen"
Deactivated="Window_Deactivated">
<Grid>
</Grid>
</Window>
Code
private void Window_Deactivated(object sender, EventArgs e)
{
this.Close();
}
If you call the DialogBox with ShowDialog the background window (which is the MainWindow) will be disabled until you close the DialogBox and it won't get any click, so there is no way for you to achieve what you want unless you call the DialogBox with Show method which won't lock the background window, then you can close the DialogBox whenever MainWindow GotFocus fires.
first declare your temp_window as a filed not as a local variable:
private Window1 temp_Window;
When you open the new window don't open it as a dialog otherwise, the events on the mainWindow will not be handled use Show() instead of ShowDialog() :
private void Button_Click(object sender, RoutedEventArgs e)
{
temp_Window = new Window1();
temp_Window.Show();
}
On your main window handle the mouseDown event:
MouseDown="MainWindow_OnMouseDown"
and close the tempWindow in the handler
private void MainWindow_OnMouseDown(object sender, MouseButtonEventArgs e)
{
tempWindow .Close();
}
You have to declare your second window as a global variable and call the .Hide() and .Show() commands like that:
MainWindow:
public partial class MainWindow : Form
{
private Dialog m_Dialog;
public MainWindow()
{
InitializeComponent();
m_Dialog = new Dialog();
this.Click += closeDialog;
}
private void closeDialog(object sender, EventArgs e)
{
m_Dialog.Hide();
}
private void button1_Click(object sender, EventArgs e)
{
m_Dialog.Show();
}
}
It works just fine.
UPDATED:
Here is the same code for WPF. I did it in WinForms previously:
public partial class MainWindow : Window
{
private Dialog m_Dialog;
public MainWindow()
{
InitializeComponent();
m_Dialog = new Dialog();
this.MouseDoubleClick += onCloseDialog;
}
private void onCloseDialog(object sender, MouseButtonEventArgs e)
{
m_Dialog.Hide();
}
private void onButton(object sender, RoutedEventArgs e)
{
m_Dialog.Show();
}
}
You can use another event instead of MouseDoubleClick of course.
If you really want to close the window you would have to use the .Close() command and call the constructor of you window everytime.
I have a strange problem in my project. There are pages made from usercontrol and menu bar (also usercontrol).
Here is my usercontrol that contains few buttons
public partial class UpperBar : UserControl
{
public UpperBar()
{
InitializeComponent();
}
public event EventHandler EventbtClicked;
private void btConnect_Click(object sender, System.Windows.RoutedEventArgs e)
{
EventbtClicked(this, e);
}
}
I added this in my page as follows:
<local:UpperBar VerticalAlignment="Top" Grid.Row="0" Height="78" Grid.ColumnSpan="3" Margin="0,2,0,0"/>
And in my page tried to call event:
public PageStatus()
{
InitializeComponent();
Plc.ExecuteRefresh += new EventHandler(RefreshLeds);
UpperBar.EventbtCliced += new EventHandler(UpperBatButtonClick);
}
protected void UpperBarButtonClick(object sender, EventArgs e)
{
//do something
}
But I can't access my event using this UpperBar.EventbtCliced, why ?
You need to access the instance of your class UpperBar in PageStatus, not the class UpperBar itself!
The easiest way for you here:
Name your UpperBar in your XAML, example:
<local:UpperBar x:Name="_myBar" x:FieldModifier="private"/>
Then use this instance in your PageStatus.xaml.cs:
public partial class MainWindow : Window {
public MainWindow()
{
InitializeComponent();
_myBar.EventbtClicked += new EventHandler(UpperBarButtonClick);
}
protected void UpperBarButtonClick(object sender, EventArgs e)
{
//do something
}
}
Now if you are working seriously in WPF, you should really learn about Databinding and MVVM, catching event this way is not the best way to do it at all.
You should use Custom Command (RoutedUICommand) rather than bubbling event from user control.
here are some steps to follow in contrast to your approach:
1: create class myCustomCommand.
namespace WpfApplication1
{
public class myCustomCommand.
{
private static RoutedUICommand _luanchcommand;//mvvm
static myCustomCommand.()
{
System.Windows.MessageBox.Show("from contructor"); // static consructor is called when static memeber is first accessed(non intanciated object)
InputGestureCollection gesturecollection = new InputGestureCollection();
gesturecollection.Add(new KeyGesture(Key.L,ModifierKeys.Control));//ctrl+L
_luanchcommand =new RoutedUICommand("Launch","Launch",typeof(myCustomCommand.),gesturecollection);
}
public static RoutedUICommand Launch
{
get
{
return _luanchcommand;
}
}
}
}
In the xaml of UserControl:
<UserControl x:Class="WpfApplication1.UserControl1"
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:CustomCommands="clr-namespace:WpfApplication1"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300">
<UserControl.CommandBindings>
<CommandBinding Command="CustomCommands:myCustomCommand.Launch" Executed="CommandBinding_Executed">
</CommandBinding>
</UserControl.CommandBindings>
<Grid >
<TextBox Name="mytxt" Height="30" Width="60" Margin="50,50,50,50" ></TextBox>
<Button Name="b" Height="30" Width="60" Margin="109,152,109,78" Command="CustomCommands:ZenabUICommand.Launch"></Button>
</Grid>
Now in User control code
Handle command_executed
private void CommandBinding_Executed(object sender, ExecutedRoutedEventArgs e)
{
mytxt.Text = "invoked on custom command";
}
}
}