I have a window with a button and usercontrol. Now when I click on the button, the raised event, I wanna catch on usercontrol. I guess this principle it called event tunneling. But I don't know too, if this way would be the right way to catch the button click event.
Example
<Window x:Class="EventTunneling.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:uc="clr-namespace:EventTunneling"
Title="MainWindow" Height="350" Width="525">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="30"></RowDefinition>
<RowDefinition Height="Auto"></RowDefinition>
</Grid.RowDefinitions>
<Button Grid.Row="0" Content="Click me"></Button>
<uc:Control Grid.Row="1"></uc:Control>
</Grid>
</Window>
UserControl
<UserControl x:Class="EventTunneling.Control"
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"
d:DesignHeight="300" d:DesignWidth="300">
<Grid>
</Grid>
</UserControl>
Now i want to catch the event on the partial class of usercontrol, that was raised from main window. How can i do that?
namespace EventTunneling
{
/// <summary>
/// Interaction logic for Control.xaml
/// </summary>
public partial class Control : UserControl
{
public Control()
{
InitializeComponent();
}
}
}
Unfortunately, you're doing this all wrong. First, you need to data bind your collection from the code behind to the DataGrid.ItemsSource property. Then, when the Button is clicked, just handle it in the code behind, where you have access to the collection and simply save it however you like:
In UserControl:
<DataGrid ItemsSource="{Binding Items}" ... />
In MainWindow:
<Button Grid.Row="0" Content="Click me" Click="Button_Click" />
In code behind (you should implement the INotifyPropertyChanged interface on this property):
public ObservableCollection<YourDataType> Items { get; set; }
...
private void Button_Click(object sender, RoutedEventArgs e)
{
SaveAsXml(Items);
}
In code behind constructor (or set it in any valid way you prefer):
DataContext = this;
Make a public event on the window.
Fire the event when the button is clicked.
Subscribe to the event from your control
you can simply do this when the button is clicked make a call to a public method in your user Control :
namespace EventTunneling
{
public partial class Control : UserControl
{
public Control()
{
InitializeComponent();
}
public void CatchEventOnUserControl()
{
//do your job here
//....
}
}
}
then when the button is clicked call the CatchEventOnUserControl method
<Window x:Class="EventTunneling.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:uc="clr-namespace:EventTunneling"
Title="MainWindow" Height="350" Width="525">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="30"></RowDefinition>
<RowDefinition Height="Auto"></RowDefinition>
</Grid.RowDefinitions>
<Button Grid.Row="0" Content="Click me" Click="Button_Click"></Button>
<uc:Control x:Name="mycontrol" Grid.Row="1"></uc:Control>
</Grid>
and the code behind is
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void Button_Click(object sender, RoutedEventArgs e)
{
mycontrol.CatchEventOnUserControl();
}
}
Related
i have two usercontrols which is bound in MainWindow. What i want when MainWindow is Loaded, textbox1 which is in usercontrol1 should be focused automatically, and when i hit down arrow from keyboard textbox2 which is in usercontrol2 should be focused. And again on up arrow focus textbox1.
Below is MainWindow's xaml code`
<Window x:Class="FocusTextboxesFromMain.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:FocusTextboxesFromMain" Loaded="Window_Loaded"
mc:Ignorable="d"
Title="Focus Textboxes Testing" Height="450" Width="800">
<Grid>
<StackPanel>
<local:UserControl1/>
<local:UserControl2/>
</StackPanel>
</Grid>
Below is Usercontrol1's xaml code`
<UserControl
x:Class="FocusTextboxesFromMain.UserControl1"
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:FocusTextboxesFromMain"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
Width="800"
Height="200"
mc:Ignorable="d">
<Grid Background="Red">
<TextBlock
FontSize="50"
Foreground="White"
Text="UserControl1" />
<TextBox
x:Name="textbox1"
Width="200"
HorizontalAlignment="Center"
VerticalAlignment="Center"
FontSize="25" />
</Grid>
and below is Usercontrol2's xaml code`
<UserControl
x:Class="FocusTextboxesFromMain.UserControl2"
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:FocusTextboxesFromMain"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
Width="800"
Height="200"
mc:Ignorable="d">
<Grid Background="Green">
<TextBlock
FontSize="50"
Foreground="White"
Text="UserControl2" />
<TextBox
x:Name="textbox2"
Width="200"
HorizontalAlignment="Center"
VerticalAlignment="Center"
FontSize="25" />
</Grid>
please note that i want all my logic in MainWindow Class, MvvM should not be involved
namespace FocusTextboxesFromMain
{
using System.Windows;
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void Window_Loaded(object sender, RoutedEventArgs e)
{
// Focus textbox1 of usercontrol1
}
// on down arrow then Focus textbox2 of usercontrol2
// and again on up arrow then Focus textbox1 of usercontrol1
}
}
sorry for bad explanation, thanks in advance
You can find what you are looking for in the answer of this question:
Moving to next control on Enter keypress in WPF
Just follow the code and create related event
public MainWindow()
{
InitializeComponent();
this.PreviewKeyDown += new KeyEventHandler(txtFirst_KeyDown);
this.PreviewKeyDown += new KeyEventHandler(txtSecond_KeyDown);
}
private void txtFirst_KeyDown(object sender, KeyEventArgs e)
{
if (e.Key == System.Windows.Input.Key.Down)
{
txtSecond.Focus();
}
}
private void txtSecond_KeyDown(object sender, KeyEventArgs e)
{
if (e.Key == System.Windows.Input.Key.Up)
{
txtFirst.Focus();
}
}
private void Window_Loaded_1(object sender, RoutedEventArgs e)
{
txtFirst.Focus();
}
I'm not sure if I asked the question correctly, because I am not finding the answer by searching the internet. I am creating a wizard window. I have a window that has a title at the top and buttons at the bottom that will stay there throughout changing the pages. So in the xaml.cs for the window, I have a list of UserControls that will contain all of the views for the wizard. I also have a property/field that holds the current view. I want to create a xaml UserControl tag that binds to the current view property. It should change when I change the current view property (I have already implemented the INotifyChanged interface). The current view property will be changed by c#. Here is the code that I have(simplified to show whats needed), and when I run it nothing shows in the view area:
WizardWindow.xaml:
<Window x:Class="WizardWindow.WizardWindow"...>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="70"/>
<RowDefinition Height="*"/>
<RowDefinition Height="50"/>
</Grid.RowDefinitions>
<Grid Grid.Row="0">
<TextBlock Text="Wizard Title"/>
</Grid>
<Grid Grid.Row="1">
<Border Name="WizardWindowPageContent" Margin="5" BorderBrush="Black" BorderThickness="1">
<!--This is what I have tried but isn't working -->
<UserControl Content="{Binding CurrentView}" />
</Border>
</Grid>
<Grid Grid.Row="2" Name="WizardButtons">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Button Grid.Column="0">Cancel</Button>
<Button Grid.Column="2">Back</Button>
<Button Grid.Column="3">Next</Button>
</Grid>
</Grid>
</Window>
WizardWindow.xaml.cs:
using System;
...
using System.Windows.Controls;
namespace WizardWindow
{
public partial class WizardWindow : Window, INotifyPropertyChanged
{
// List of views in the config window
private List<UserControl> views;
// Current view showing in the window
private UserControl currentView;
public UserControl CurrentView
{
get { return currentView; }
set
{
currentView = value;
OnPropertyChanged("CurrentView");
}
}
// Used to keep track of the view index
private int viewIndex;
public WizardWindow ()
{
InitializeComponent();
// Set the screen to the center of the screen
WindowStartupLocation = System.Windows.WindowStartupLocation.CenterScreen;
views = new List<UserControl>();
views.Add(new FirstWizardPage(this));
viewIndex = 0;
CurrentView = views[viewIndex];
}
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(string info)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(info));
}
}
}
}
FirstWizardPage.xaml:
<!-- This should show up in the window -->
<UserControl x:Class="WizardWindow.FirstWizardPage" ... >
<Grid>
<TextBlock>Lorem Ipsum ...</TextBlock>
</Grid>
</UserControl>
FirstWizard.xaml.cs:
using System.Windows.Controls;
namespace WizardWindow
{
public partial class FirstWizardPage : UserControl
{
public FirstWizardPage(WizardWindow window)
{
InitializeComponent();
}
}
}
The given possible duplicate, Window vs Page vs UserControl for WPF navigation?
, is a good solution if I wanted to rewrite my program. However, it is not a solution to my exact problem. Someone else might have a similar problem and need this solution.
you need to use this:
<ContentControl x:Name="MyView"
Content="{Binding CurrentView}" />
It should work for you, but it's not MVVM.
You must binding on property DataContext of your user control, if you want to use MVVM.
I Will show little example for you:
It is MainWindow.xaml:
<Window x:Class="WpfApp2.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:WpfApp2"
xmlns:cefSharp="clr-namespace:CefSharp.Wpf;assembly=CefSharp.Wpf"
mc:Ignorable="d"
Title="MainWindow" Height="350" Width="525">
<Grid>
<local:Foo DataContext="{Binding UserControlViewModel}"/>
</Grid>
It is MainWindow.xaml.cs:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
DataContext = new MainWindowViewModel();
}
}
It is MainWindowViewModel.cs:
public class MainWindowViewModel
{
public MainWindowViewModel()
{
UserControlViewModel = new UserControlViewModel{ Name = "Hello World" };
}
public UserControlViewModel UserControlViewModel { get; }
}
It is Foo.xaml:
<UserControl x:Class="WpfApp2.Foo"
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:WpfApp2"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300">
<Grid>
<TextBlock Text="{Binding Name}"/>
</Grid>
It is FooUsercontrolViewModel.cs
public class FooUserControlViewModel
{
public string Name { get; set; }
}
I have the following, working code:
<Window x:Class="GemTowerDefense.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:GemTowerDefense"
mc:Ignorable="d"
Title="Gem Tower Defense" Height="670" Width="800"
Loaded="Window_Loaded">
<Window.Resources>
<local:GameControl x:Key="MyGameControl"/>
</Window.Resources>
<Grid>
<Border Background="Gray" Height="600" Width="600" Margin="3,26,189,3">
<local:GameControl/>
</Border>
</Grid>
</Window>
Here you can see that I have the "local:GameControl" duplicated. This is because I only had the GameControl inside the "Grid" and the "Border". Everything worked fine, I implemented OnRender inside GameControl, it draws out everything I want it to.
My problem is, that I want this local:GameControl to interact with the MainWindow's buttons:
<Button Click="Life_Button_Click" Content="Buy 1 Life" HorizontalAlignment="Left" Margin="608,26,0,0" VerticalAlignment="Top" Width="170"/>
<Button Click="Upgrade_Button_Click" Content="Upgrade Chances" HorizontalAlignment="Left" Margin="608,51,0,0" VerticalAlignment="Top" Width="170"/>
<Button Click="Place_Button_Click" Content="Place Gems" HorizontalAlignment="Left" Margin="608,156,0,0" VerticalAlignment="Top" Width="170"/>
So I added this inside "Grid". But inside the Button Click Event Handlers, I don't know how to reach GameControl.
What I want to do is something like this:
public partial class MainWindow : Window
{
GameControl control { get; set; }
private void Life_Button_Click(object sender, RoutedEventArgs e)
{
control.BuyLife();
}
private void Window_Loaded(object sender, RoutedEventArgs e)
{
control = Application.Current.MainWindow.Resources["MyGameControl"] as GameControl;
}
}
How can I achieve this to work?
This doesn't work obviously, because I cannot name GameControl with an x:Key, only if I add it as a resource, but then I dont know how to refer to it as a FrameworkElement in the xaml code.
Give the control an x:Name:
<Border Background="Gray" Height="600" Width="600" Margin="3,26,189,3">
<local:GameControl x:Name="control" />
</Border>
You can then access it in the code-behind:
private void Life_Button_Click(object sender, RoutedEventArgs e)
{
control.BuyLife();
}
I want to just navigate between pages
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void RouteItems(object sender, RoutedEventArgs e)
{
NavigationService nav = NavigationService.GetNavigationService(this);
nav.Navigate(new ItemsPage());
}
}
GetNavigationService returns null
<Window x:Class="Special.MainWindow"
Name="Window"
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:Special"
mc:Ignorable="d"
Title="MainWindow" WindowState="Maximized">
<Button Content="Go" Click="RouteItems"/>
</Window>
I don't want to use other method (changing Content).
EDIT
this.NavigationService is undefined
You need to, first add a Frame to your Xaml:
<Grid >
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Frame x:Name="MainFrame"></Frame>
<Button Content="Go" Click="RouteItems" Grid.Row="1"/>
</Grid>
then after you add your xaml pages, use the MainFrame to navigate:
private void RouteItems(object sender, RoutedEventArgs e)
{
MainFrame.NavigationService.Navigate(new Uri("Page1.xaml", UriKind.RelativeOrAbsolute));
}
I'm trying to have a window with a <Menu> element in it bound to a dependencyProperty:
Here is my Xaml:
<Window x:Class="attachement.xWindow"
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"
d:DesignHeight="300" d:DesignWidth="300">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Menu/>
<ToolBarTray x:Name="ToolBarTray" Grid.Row="1">
</ToolBarTray>
<ScrollViewer Grid.Row="2">
</ScrollViewer>
</Grid>
</Window>
and here is my code behind:
public partial class xWindow : Window
{
public Menu Menu
{
get { return (Menu)GetValue(MenuProperty); }
set { SetValue(MenuProperty, value); }
}
public static readonly DependencyProperty MenuProperty = DependencyProperty.Register("Menu", typeof(Menu), typeof(xWindow), new UIPropertyMetadata(0));
public xWindow()
{
InitializeComponent();
}
}
now my question is: how can I bind the <Menu> element in my xaml to the dependency property in code behind so that when I do "myXwindow.Menu = new Menu(){...};" the menu is updated in the window?
thanks
NB: I tried setting the xaml like this : <Menu x:Name="Menu"> and remove the dp in c# so taht I could access directly the Menu defined in xaml, wich seems to work (no build or run error) but does not allow me to set it anew after the window has been displayed
You can wrap your Menu in some other control
<ContentControl x:Name="_menuContainer">
<Menu/>
</ContentControl>
And then write your property like this:
public Menu Menu
{
get { return (Menu)_menuContainer.Content; }
set { _menuContainer.Content = value; }
}