Frame navigation with key binding command? - wpf

I’d like to load a page into frame with key binding using ‘Ctrl’ and ‘H’ buttons from a keyboard. I can load page. It is working now. I am wondering if I can just override UriSource link with what it is now into frame. I assign UriSources in XML and I need to override one instance instead of introducing new UriSource to keep the index not changed. Any ideas? Thank you in advance!
public partial class MainWindow : Window
{
public static RoutedUICommand LoadShareSelectedCommand = new RoutedUICommand();
public MainWindow()
{
InitializeComponent();
this.CommandBindings.Add(new CommandBinding(LoadShareSelectedCommand, LoadShareSelectedCommandExecuted));
KeyGesture kg = new KeyGesture(Key.H, ModifierKeys.Control);
InputBinding ib = new InputBinding(LoadShareSelectedCommand, kg);
this.InputBindings.Add(ib);
}
private void LoadShareSelectedCommandExecuted(object sender, ExecutedRoutedEventArgs e)
{
this.ContentFrame.NavigationService.Navigate(new Uri("Pages/SharesSelected.xaml", UriKind.Relative));
}
private void LoadShareSelectedCommand_CanExecute(object sender, CanExecuteRoutedEventArgs e)
{
e.CanExecute = true;
}
private void NavShareSelected(object sender, RoutedEventArgs e)
{
this.ContentFrame.NavigationService.Navigate(new Uri("Pages/SharesSelected.xaml", UriKind.Relative));
}
}

Related

WPF access a page control from MainWindow class

I have created a WPF app that has navigation between two pages.
I want a control(a groupbox) from one of the pages to be hidden by default and to be able to enable it when pressing a keycombo.
Home1 is the name of the page and bgdb is the name of the groupbox.
Home1_Loaded is hooked up to the page loading inside a frame in MainWindow
public void Home1_Loaded(object sender, RoutedEventArgs e)
{
bdgb.Visibility = Visibility.Collapsed;
}
What modifications need to be done so I can access bgdb from MainWindow class and unhide it via a keycombo (ex Ctrl+B) ?
this is the code for mainwindow loading the homepage by default
private void Window_Initialized(object sender, EventArgs e)
{
Main.Content = new home();
Main.Navigate(new Uri("home.xaml", UriKind.RelativeOrAbsolute));
}
If you are hosting the Page in a Frame element in the MainWindow, you could cast the Content property of the Frame to Home1 and then access any of its members, e.g.:
Home1 home1 = e.Content as Home1;
if (home1 != null)
home1.bdgb.Visibility = Visibility.Collapsed;
MainWindow.xaml:
<Frame x:Name="frame" />
You could for example handle the Navigated event for the Frame:
private void Window_Initialized(object sender, EventArgs e)
{
Main.Content = new home();
Main.Navigated += Main_Navigated;
Main.Navigate(new Uri("home.xaml", UriKind.RelativeOrAbsolute));
}
private void Main_Navigated(object sender, System.Windows.Navigation.NavigationEventArgs e)
{
home home1 = Main.Content as home;
if (home1 != null)
home1.bdgb.Visibility = Visibility.Collapsed;
Main.Navigated -= Main_Navigated;
}

display value from combobox on different page in textbox

I am trying to display the selection from a combobox in a textbox on a different page when a button is clicked. I thinking of using NavigationService, but I am not sure if that is the right way to go or not. In this part of the code I am getting the correct value and for testing I am displaying in messagebox, and that is working.
private void Button_Click(object sender, RoutedEventArgs e)
{
itemSelection = SubItemBox.Text;
NavigationService.Navigate(
new Uri("/Display.xaml?Message=" + itemSelection,
UriKind.Relative)
);
MessageBox.Show(itemSelection);
}
I am having an issue figuring out where to go next, I can't figure out how to get the itemSelection to dispaly in Display.xaml
namespace CateringDisplay
{
public partial class Display : Page
{
string itemSelection;
public Display()
{
InitializeComponent();
}
private void Page_Loaded(object sender, RoutedEventArgs e)
{
}
}
}
Any help would be appreciated as I am trying to learn WPF
Instead of using navigation, you could try the Event Aggregator (https://msdn.microsoft.com/en-us/library/ff921122.aspx):
You define an event:
public class SelectionChangedEvent : PubSubEvent<string> { }
You subscribe to the event in the Display Page
public partial class Display : Page
{
string itemSelection;
public Display()
{
InitializeComponent();
IEventAggregator eventAggregator = Locator.GetInstance<IEventAggregator>();
eventAggregator.GetEvent<SelectionChangedEvent>().Subscribe(OnSelectionChanged);
}
private void OnSelectionChanged(string obj)
{
itemSelection = obj;
}
private void Page_Loaded(object sender, RoutedEventArgs e)
{
}
}
The event handler updates your item selection using the event payload. Finally you fire the event from the button click event handler:
private void Button_Click(object sender, RoutedEventArgs e)
{
itemSelection = SubItemBox.Text;
IEventAggregator eventAggregator = Locator.GetInstance<IEventAggregator>();
eventAggregator.GetEvent<SelectionChangedEvent>().Publish(itemSelection);
MessageBox.Show(itemSelection);
}
Hope it helps.

Adding tabpage to a tabcontrol which is on another form on click of button

I have a winform called form1 which has tabcontrol with some tabpages and one button on clicking of which I open another form called form2. I want to add tabpage to form1 tabcontrol on click of button which is present on form2.
Assumed that you have not created a tabcontrol at runtime in the Firstform.
In FirstForm.cs
private void button1_Click(object sender, EventArgs e)
{
SecondForm obj = new SecondForm(this);
obj.Show();
}
public void addTabpage()
{
TabPage tbpg = new TabPage();
tbpg.Text = "New tabpage";
tabControl1.TabPages.Add(tbpg);
this.BringToFront();
}
In SecondForm.cs, change the constructor as follows
static FirstForm objpub;
public SecondForm(FirstForm obj)
{
InitializeComponent();
objpub = obj;
}
private void button1_Click(object sender, EventArgs e)
{
if (objpub != null)
{
objpub.addTabpage();
}
}
If this answer does not meet your problem, then provide your code.
Cheers !

How to make a general wpf mvvm window open/close eventhandler?

This is my current App.xaml.cs
Its looks simple for one or two, but I have 7-8 windows.
Is there a clever way to make this a little more general and better?
public App()
{
_ViewModel = new MyAppViewModel();
_ViewModel.OpenXXXWindowEvent += new EventHandler(ViewModel_OpenXXXWindow);
_ViewModel.OpenYYYWindowEvent += new EventHandler(ViewModel_OpenYYYWindow);
...
}
private void ViewModel_OpenXXXWindow(object sender, EventArgs e)
{
_XXXWindow = new XXXWindow();
_XXXWindow.DataContext = _ViewModel;
_XXXWindow.ShowDialog();
}
private void ViewModel_CloseXXXWindow(object sender, EventArgs e)
{
if (_XXXWindow != null)
_XXXWindow.Close();
}
private void ViewModel_OpenYYYWindow(object sender, EventArgs e)
{
_YYYWindow = new YYYWindow();
_YYYWindow.DataContext = _ViewModel;
_YYYWindow.ShowDialog();
}
private void ViewModel_CloseYYYWindow(object sender, EventArgs e)
{
if (_YYYWindow != null)
_YYYWindow.Close();
}
...
Too much XAML code-behind is a signal that you're somehow breaking the MVVM pattern. A ViewModel receiving EventArgs is a no-no too.
To open/close dialogs I tend to use a messaging system, for example, the one provided by MVVM Light.
With a messaging system (using MVVM Light) you do something like this:
In your ViewModel:
private void SomeMethodThatNeedsToOpenADialog()
{
Messenger.Default.Send(new OpenDialogXMessage());
}
And in your View:
Messenger.Default.Register<OpenDialogXMessage>(this, (msg) => {
new DialogX().ShowDialog();
});
Some relevant links:
How to open a new window using MVVM Light Toolkit
Show dialog with MVVM Light toolkit
Well it's not a event handler solution, but how about a Binding solution? Unfortunately, you cannot bind Window.DialogResult, which causes the window to close, when the value is set. But you could create an AttachedProperty which can be bound to a property on the underlying ViewModel and sets the not bindable property, when its value is set. The AttachedProperty looks like this.
public class AttachedProperties
{
public static readonly DependencyProperty DialogResultProperty =
DependencyProperty.RegisterAttached("DialogResult", typeof (bool?), typeof (AttachedProperties), new PropertyMetadata(default(bool?), OnDialogResultChanged));
private static void OnDialogResultChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var wnd = d as Window;
if (wnd == null)
return;
wnd.DialogResult = (bool?) e.NewValue; //here the not bindable property is set and the windows is closed
}
public static bool? GetDialogResult(DependencyObject dp)
{
if (dp == null) throw new ArgumentNullException("dp");
return (bool?)dp.GetValue(DialogResultProperty);
}
public static void SetDialogResult(DependencyObject dp, object value)
{
if (dp == null) throw new ArgumentNullException("dp");
dp.SetValue(DialogResultProperty, value);
}
}
The AttachedProperty can be used like this
<Window x:Class="AC.Frontend.Controls.DialogControl.Dialog"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:hlp="clr-namespace:AC.Frontend.Helper"
hlp:AttachedProperties.DialogResult="{Binding DialogResult}">
<!-- put your content here -->
</Window>
Now you can use a Command to set the DialogResult property of the VM, which is the DataContext of the Window.

When Raising an MouseButtonEvent from a UserControl the MainWindow have no access to MouseButtonEventArgs

Still climbing up the steep WPF Mountain, and suffering.
I have defined a UserControl, and my MainWindow needs to retrieve the MouseButtonEventArgs coming from a control inside the UserControl (like the mouse e.GetPosition for instance)
In the UserControl code behind, I have done the Registrations and I Raise the bubbling event.
public static readonly RoutedEvent MyButtonDownEvent = EventManager.RegisterRoutedEvent("MyMouseButtonDown", RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(MyUserControl));
public event RoutedEventHandler MyButtonDown {
add { AddHandler(MyButtonDownEvent, value); }
remove { RemoveHandler(MyButtonDownEvent, value); }
}
private void MyMouseButtonDownHandler(object sender, MouseButtonEventArgs e) {
RaiseEvent(new RoutedEventArgs(MyButtonDownEvent ));
}
Now in my MainWindow I declare the UserControl like this:
<local:MyUserControl MouseDown="MyUserControl_MouseDown"/>
And this code behind
private void MyUserControl_MouseDown(object sender, RoutedEventArgs e)
And I receive the events from the UserControl, but the Args are RoutedEventArgs (which is normal) but I dont have access to the MouseButtonEventArgs that I need to get the mouse e.GetPosition.
What elegant solution would you suggest in this case ?
Why do you define your own MouseDown event while UserControl already has a normal MouseDown event?
Anyway, if you define an event to use a RoutedEventHandler it is hardly surprising that you'll end up being stuck with a RoutedEventHandler. You declared it like this:
public static readonly RoutedEvent MyButtonDownEvent = EventManager.RegisterRoutedEvent("MyMouseButtonDown", RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(MyUserControl));
Notice the bit where it says typeof(RoutedEventHandler)?
If i am not mistaken your code should look like this instead:
public static readonly RoutedEvent MyButtonDownEvent =
EventManager.RegisterRoutedEvent
("MyButtonDown",
RoutingStrategy.Bubble,
typeof(MouseButtonEventHandler),
typeof(MyUserControl));
public event MouseButtonEventHandler MyButtonDown
{
add { AddHandler(MyButtonDownEvent, value); }
remove { RemoveHandler(MyButtonDownEvent, value); }
}
Example of how to propagate an existing MouseDown event to the custom event:
InitializeComponent();
this.MouseDown += (s, e) => {
RaiseEvent(new MouseButtonEventArgs(e.MouseDevice, e.Timestamp, e.ChangedButton)
{
RoutedEvent = MyButtonDownEvent
});
};
I think I finally got it (at least I hope):
If I write in the code behind:
public event EventHandler<MouseButtonEventArgs> MyRightButtonDownHandler;
public void MyRightButtonDown(object sender, MouseButtonEventArgs e) {
MyRightButtonDownHandler(sender, e);
}
Then in the consumer (MainWindow) XAML:
<local:GlobalDb x:Name="globalDb" MyRightButtonDownHandler="globalDb_MyRightButtonDownHandler"/>
And in the consumer code behind:
private void globalDb_MyRightButtonDownHandler(object sender, MouseButtonEventArgs e) {
Console.WriteLine("x= " + e.GetPosition(null).X + " y= " + e.GetPosition(null).Y);
}
Please tell me if you have a better solution (By design policy - rules established where I work - all the event handling of my application MUST appear in the XAML).
Thanks again for your help,

Resources