I would like help regarding how to handle events from controls in a ResourceDictionary (e.g. Styles.xaml) without using ResourceDictionary Code-Behind (e.g. Styles.xaml.cs), mainly because I want the Styles.xaml to just be there for styling.
My scenario is, I have a Custom Page which uses a ResourceDictionary for DataTemplate styles (I am using a TemplateSelector). However, the problem I am currently having is for handling events. For example:
I have this in my Styles.xaml:
.
.
<Button Click="Button_Click"/>
.
And I declare this in the CustomPage.xaml.cs :
private void Button_Click(object sender, RoutedEventArgs e)
{
// some code
}
However, it does not work. Is there any way to explicitly tell that I want to use that particular Event Handler for the button's click event? Additionally, is it possible to have different handlers for each page e.g.
CustomPage2.xaml.cs:
private void Button_Click(object sender, RoutedEventArgs e)
{
// some different code from CustomPage.xaml.cs
}
Thank you!
The answer is simple: do not handle events such a way. Use bindings instead (especially, if you're using data templates). E.g., for Button:
<Button Command="{Binding MyCommand}">
where MyCommand is a ICommand-implemented instance from your data context.
If you're not familiar with data bindings in WPF, start read from here.
Related
Very new to XAML/C# and I wanted to know how to send a parameter from XAML to a C# method.
This TextBlock <TextBlock PreviewMouseDown="changeFont" /> would call a method with the signature
changeFont(object sender, MouseButtonEventArgs e)
But I wanted to know if I could add a parameter to the signature like this:
changeFont(object sender, MouseButtonEventArgs e, string Name)
and send an argument via XAML
<TextBlock PreviewMouseDown="changeFont('Bob')" />
There is no way to do this in WPF; that's not how it's designed. If you need additional information to process the PreviewMouseDown event, you'll have to get it from somewhere else.
sender gives you the element that raised the event (in your case the TextBlock). So one option is to use the Tag property:
<TextBlock PreviewMouseDown="changeFont" Tag="Bob"/>
changeFont(object sender, MouseButtonEventArgs e)
{
var moreInfo = ((TextBlock)sender).Tag;
...
}
One other thing to look into is the use of commands. Commands let you add a CommandParameter. Certain controls Command property that can be set to execute said command when they are interacted with, like when a Button is clicked. This isn't the case for TextBlock, but it might be useful later on.
No, this is not possible. The XAML snippet PreviewMouseDown="changeFont" attaches an event handler to the PreviewMouseDown event. And the event handler changeFont needs to have a fixed signature. You cannot simply add a parameter.
That being said, changeFont does not sound like the name of an event handler anyway. It is an action. If you have multiple of these text blocks and you want to re-use the changeFont method, you could define separate event handlers that each call the action:
<TextBlock PreviewMouseDown="BobPreviewMouseDown" />
<TextBlock PreviewMouseDown="AlicePreviewMouseDown" />
void BobPreviewMouseDown(object sender, MouseButtonEventArgs e)
{
changeFont("Bob");
}
void AlicePreviewMouseDown(object sender, MouseButtonEventArgs e)
{
changeFont("Alice")
}
That's one option. Of course, there are many others. You could, e.g., also have a single event handler and derive the parameter from the sender object (e.g. with an attached property or similar).
You should use Command and CommandParameter properties. It's better to use MVVM pattern and link to a command from your ViewModel, but if you don't use it, you also can use the Routed Command system and set an event handler for the command via CommandBinding property:
<Button Content="Bob button"
Command="{Binding ChangeFont}"
CommandParameter="Bob" />
I used <Button /> here, but if you want it to look like a <TextBlock /> you can change its appearance by changing it's Template property. But remember, that user expects that a button behaves like a button while a text block behaves like a text block.
For my current project I need to capture button pressed and release events in Xamarin.Forms. But I want to keep things loosely coupled using Prism.
At first I used the Command property of the , like so:
<Button x:Name="ButtonForward" Command="{Binding MoveUpCommand}" />
But the Command property only fires when the button is released. To make seperate pressed and released actions I used the events in XAML:
<Button x:Name="ButtonForward" Pressed="ButtonForward_Pressed" Released="ButtonMove_Released"/>
And called the Commands manually in the event handlers in code behind:
private void ButtonMove_Released(object sender, System.EventArgs e)
{
var vm = BindingContext as DirectControlViewModel;
if (vm.MoveStopCommand.CanExecute(null))
vm.MoveStopCommand.Execute(null);
}
private void ButtonForward_Pressed(object sender, System.EventArgs e)
{
var vm = BindingContext as DirectControlViewModel;
if (vm.MoveUpCommand.CanExecute(null))
vm.MoveUpCommand.Execute(null);
}
The problem is that it isn't loosely coupled anymore, since the View now has to know its ViewModel.
Is there a way to have a button that does have seperate commands for pressed and released events, keeping the View and ViewModel loosely coupled? Any help would be appreciated.
Use the EventToCommandBehavior on the Button. This will allow you take advantage of any Event on anything you're working with and Execute a Command when the event is fired.
<Button>
<Button.Behaviors>
<prism:EventToCommandBehavior EventName="Pressed"
Command="{Binding PressedCommand}" />
<prism:EventToCommandBehavior EventName="Released"
Command="{Binding ReleasedCommand}" />
</Button.Behaviors>
</Button>
Note there are additional properties that you can utilize if you have some sort of Parameter that you would like to pass the Command which may be a property in the EventArgs, or something else entirely that you'd like to bind to or specify.
I have a ToggleButton in MainPage.xaml:
<ToggleButton x:Name="ColorToggle" Background="{Binding Background, ElementName=LayoutRoot}" ToolTipService.ToolTip="Change Toolbar Color">
...yet when I try to access it from MainPage.xaml.cs:
private void SaveAppBarColorSelected(object sender, TappedRoutedEventArgs e)
{
PhotraxUtils.SetLocalSetting(PhotraxConsts.APPBARBUTTON_COLOR, ColorToggle.Background.ToString());
}
...I get, "The name 'ColorToggle' does not exist in the current context"
Why is that?
Only controls from the root context are available in code-behind. If you place your named control inside a template, it won't be available. You can traverse the controls tree using VisualTreeHelper and other methods in this case.
Or better yet, just use MVVM.
I am developing a desktop application using Modern UI for WPF. I try to refresh my tab page when I go to a new tab page, but I couldn't.
I want to refresh my MUI WPF tab page when I go to another page using my tab controller.
Can anyone help me?
I'm not quite sure what you mean exactly, but by calling InvalidateVisual() on a control, you can force a visual refresh of it if that's what you're after, as it sounds like you've got a WPF control that isn't being updated when the data is changing.
Based on the MSDN documentation, this:
Invalidates the rendering of the element, and forces a complete new layout pass. OnRender is called after the layout cycle is completed.
For example:
var grid = new Grid();
// Our grid should refresh after this,
// although in normal circumstances it would by default regardless.
grid.InvalidateVisual();
I hope that this is of use.
You can use SelectionChanged event to handle this. You can refresh MUI WPF tab page by using SelectionChanged.
<TabControl x:Name="MyTab" SelectionChanged="MyTabControl_SelectionChanged">
<TabItem x:Name="TabItem1" Header="Tab 1"/>
<TabItem x:Name="TabItem2" Header="Tab 2"/>
<TabItem x:Name="TabItem3" Header="Tab 3"/>
</TabControl>
Then you can access to each TabItem at the event:
private void MyTabControl_SelectionChanged(object sender, SelectionChangedEventArgs e){
if (TabItem1.IsSelected){}
if (TabItem2.IsSelected){}
if (TabItem3.IsSelected){}
}
While the selected answer is ok, it might not be the best way to go about it.
MUI includes a content navigation framework that handles content loading, unloading and history navigation based on link uris. If you want your content to be aware of navigation events such as loaded and unloaded events, you'll need to implement an interface.
Make your content navigation aware by implementing the IContent interface available in the FirstFloor.ModernUI.Windows namespace.
A simple Example is :
public class MyContent : UserControl, IContent
{
public void OnFragmentNavigation(FragmentNavigationEventArgs e)
{
}
public void OnNavigatedFrom(NavigationEventArgs e)
{
}
public void OnNavigatedTo(NavigationEventArgs e)
{
//Refresh your page here
}
public void OnNavigatingFrom(NavigatingCancelEventArgs e)
{
// ask user if navigating away is ok
if (ModernDialog.ShowMessage("Navigate away?", "navigate", MessageBoxButton.YesNo) == MessageBoxResult.No) {
e.Cancel = true;
}
}
}
I've been trying to create my generic ReadOnlyCheckBox style/template but I'm having a problem with the binding to the data. In the example here:
A read-only CheckBox in C# WPF
you bind directly to the data from the ControlTemplate definition, but of course this is not really what I want, as I want to be able to declare the new checkbox something like this:
<CheckBox x:Name="uiComboBox" Content="Does not set the backing property, but responds to it."
Style="{StaticResource ReadOnlyCheckBoxStyle}" IsChecked="{Binding MyBoolean}" Click="uiComboBox_Click"/>
Except of course when I do this and then set the event trigger on the bullet to be a TemplateBinding of IsChecked I have exactly what I started with! I guess I don't understand why setting the binding directly in the bullet is different from setting IsChecked and then binding to that, isn't the TemplateBinding just a way of referencing what is set in the properties of the control being created? How is the Click triggering the UI update even tho the data does not get updated? Is there a trigger for Click I can override to stop the update?
I got all the DictionaryResource stuff working fine so I am happy with that, cheers for the pointer.
The other thing I was curious about was if it is possible to reduce my Control/Style template by using the BasedOn parameter in the style, then I would only override the things I actually need to change rather than declaring a lot of stuff that I think is part of the standard template anyway. I might have a play with this.
Cheers
ed
The problem you're running into is that you're trying to use DataBinding where you shouldn't.
I disagree with other answers you've been getting in the link you've posted. While the ControlTemplate solutions appear neat and tidy, they don't get at the heart of your problem, which is, you're trying to use a single control (a CheckBox) to do two different things: show state (e.g. checked/unchecked) and perform logic (remote device, etc.).
ControlTemplates are designed to change the way a control appears, but not behaves. You're trying to change behavior, not appearance. See "What Is a ControlTemplate?" here for more details
To do that, you're going to have to store some forego the standard binding and handle some events:
XAML:
<Window x:Class="WPFCheckBoxClickTester.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>
<CheckBox x:Name="foo" Checked="foo_Checked"></CheckBox>
</Grid>
</Window>
Code-Behind:
using System.ComponentModel;
using System.Threading;
using System.Windows;
namespace WPFCheckBoxClickTester
{
/// <summary>
/// Interaction logic for Window1.xaml
/// </summary>
public partial class Window1 : Window
{
private BackgroundWorker _worker = new BackgroundWorker();
public Window1()
{
InitializeComponent();
foo.IsThreeState = false;
this._worker.DoWork += new DoWorkEventHandler(_worker_DoWork);
this._worker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(_worker_RunWorkerCompleted);
}
private void _worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
foo.IsChecked = true;
foo.IsEnabled = true;
return;
}
private void _worker_DoWork(object sender, DoWorkEventArgs e)
{
Thread.Sleep(500);
return;
}
private void foo_Checked(object sender, RoutedEventArgs e)
{
if( foo.IsChecked == true && this.foo.IsEnabled )
{
this.foo.IsChecked = false;
this.foo.IsEnabled = false;
this._worker.RunWorkerAsync();
}
return;
}
}
}
The above code example gives you the ability to execute some code async via BackgroundWorker -- I've put in a Thread.Sleep as a placeholder. I'm also using foo.IsEnabled as a sentinel value in foo_Checked, but you may need some extra state here to handle all of your cases (for instance, going from Checked to Unchecked).