I have a non-MVVM application. In the MainWindow, I have a TabControl with several tabs, and each tab contains a UserControl. Because those UserControls have similar features, I derive them from a base class that inherits from UserControl. Each of the UserControls has a TextBox called EdiContents. And each of them has a button:
<Button Name="Copy" Content="Copy to Clipboard" Margin="10" Click="Copy_Click" />
I would like to implement Copy_Click in the base UserControl class:
private void Copy_Click(object sender, RoutedEventArgs e)
{
System.Windows.Forms.Clipboard.SetText(EdiContents.Text);
}
But the base class doesn't know EdiContents TextBox, which is declared in each UserControl's XAML. Could you please suggest how this can be solved?
Thanks.
You can do something like this.
public partial class DerivedUserControl : BaseUserControl
{
public DerivedUserControl()
{
InitializeComponent();
BaseInitComponent();
}
}
Note that you are calling BaseInitComponent after InitializeComponent
XAML behind for derived control
<app:BaseUserControl x:Class="WpfApplication5.DerivedUserControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:app="clr-namespace:WpfApplication5"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
>
<Grid>
<Button Name="CopyButton"/>
</Grid>
</app:BaseUserControl>
In your BaseUserControl::BaseInitComponent you simply lookup the button by name and wire up the event.
public class BaseUserControl : UserControl
{
public void BaseInitComponent()
{
var button = this.FindName("CopyButton") as Button;
button.Click += new System.Windows.RoutedEventHandler(Copy_Click);
}
void Copy_Click(object sender, System.Windows.RoutedEventArgs e)
{
//do stuff here
}
}
Related
I created a UserControl called fooControl.
I would like to create another UserControl called fooControlExtended to reuse/add/override both the C# and XAML code that already exists in the base UserControl fooControl.
You can do it this way:
TestUserControl.xaml
<UserControl
x:Class="UserControls.TestUserControl"
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="using:UserControls"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d">
<StackPanel Orientation="Horizontal">
<Button
Click="Button_Click"
Content="Click" />
<TextBlock
x:Name="TextControl"
Text="TestUserControl" />
</StackPanel>
</UserControl>
TestUserControl.xaml.cs
using Microsoft.UI.Xaml.Controls;
namespace UserControls;
// You need to remove the "sealed" modifier to allow inheritance.
public /*sealed*/ partial class TestUserControl : UserControl
{
public TestUserControl()
{
this.InitializeComponent();
}
protected void UpdateText(string text)
{
this.TextControl.Text = text;
}
protected virtual void OnButtonClick()
{
UpdateText("TestUserControl clicked");
}
private void Button_Click(object sender, Microsoft.UI.Xaml.RoutedEventArgs e)
{
OnButtonClick();
}
}
TestUserControlEx.cs
namespace UserControls;
public class TestUserControlEx : TestUserControl
{
protected override void OnButtonClick()
{
this.UpdateText("TestUserControlEx clicked.");
}
}
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>
I'm developing a UWP application that involves several UserControl objects inside a Map using Windows.UI.Xaml.Navigation.
Sometimes, the user should be able to click a button in these objects to go to a new page. However, I can't access the page's frame, so I can't use the following method.
Frame.Navigate(typeof([page]));
How could I access the page frame to use the method?
Let me know of any alternatives; I've been stuck on this for most of the day! Thanks in advance for any help you guys offer!
We can let the page to navigate itself. Just define an event in your custom usercontrol and listen to the event in its parent(the page).
Take the following as an example:
Create a custom user control and put a button on it for test purpose.
In test button's click event, raise the event to navigate parent page.
In Parent page, listen to the UserControl's event and call Frame.Navigate.
MyControl's Xaml:
<UserControl
x:Class="App6.MyControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:App6"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
d:DesignHeight="300"
d:DesignWidth="400">
<Grid>
<Button x:Name="testbtn" Margin="168,134,0,134" Click="testbtn_Click">test</Button>
</Grid>
</UserControl>
MyControl's CodeBehind:
public sealed partial class MyControl : UserControl
{
public delegate void MyEventHandler(object source, EventArgs e);
public event MyEventHandler OnNavigateParentReady;
public MyControl()
{
this.InitializeComponent();
}
private void testbtn_Click(object sender, RoutedEventArgs e)
{
OnNavigateParentReady(this, null);
}
}
Navigate MainPage to SecondPage:
public MainPage()
{
this.InitializeComponent();
myControl.OnNavigateParentReady += myControl_OnNavigateParentReady;
}
private void MyControl_OnNavigateParentReady(object source, EventArgs e)
{
Frame.Navigate(typeof(SecondPage));
}
You could get a reference to the Frame from the Current Window's Content.
In your user control's code behind try:
Frame navigationFrame = Window.Current.Content as Frame;
navigationFrame.Navigate(typeof([page]));
Or, with Cast=>
((Frame)Window.Current.Content).Navigate(typeof(Views.SecondPage));
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").
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";
}
}
}