How do I assign a global initialized event? - c#

I've got this code in my App.xaml.cs:
protected override void OnStartup(StartupEventArgs e)
{
EventManager.RegisterClassHandler(typeof(TextBox), TextBox.TextChangedEvent, new RoutedEventHandler(TextBox_TextChangedEvent));
}
private void TextBox_TextChangedEvent(object sender, RoutedEventArgs e)
{
// Works
}
I would like to do something similar for the InitializedEvent.
Here's my failed attempt:
protected override void OnStartup(StartupEventArgs e)
{
EventManager.RegisterClassHandler(typeof(FrameworkElement), FrameworkElement.InitializedEvent, new EventHandler(FrameworkElement_InitializedEvent));
}
private void FrameworkElement_InitializedEvent(object sender, EventArgs e)
{
}
Is the InitializedEvent somewhere else?
Is this even possible?
I've tried using the LoadedEvent:
protected override void OnStartup(StartupEventArgs e)
{
EventManager.RegisterClassHandler(typeof(FrameworkElement), FrameworkElement.LoadedEvent, new RoutedEventHandler(FrameworkElement_LoadedEvent));
}
private void FrameworkElement_LoadedEvent(object sender, RoutedEventArgs e)
{
// Fires only for Windows
}
It only fired for Windows and not the controls inside the Windows. I did realize though; that when I added a loaded event to a Label that I had inside my Window; the global FrameworkElement_LoadedEvent fired for that Label even though my normal loaded event (That I made for the Label specifically) was empty. I've also tried these:
EventManager.RegisterClassHandler(typeof(Button), Button.LoadedEvent, new RoutedEventHandler(Button_LoadedEvent));
EventManager.RegisterClassHandler(typeof(Grid), Grid.LoadedEvent, new RoutedEventHandler(Grid_LoadedEvent));
EventManager.RegisterClassHandler(typeof(DataGrid), DataGrid.LoadedEvent, new RoutedEventHandler(DataGrid_LoadedEvent));
But they don't fire unless I add another empty loaded event on those controls specifically.
My goal is to build up a sort of a time log of every control that becomes initialized.
How can I achieve this without adding loaded events on every single control I have?
(I have a lot)

Here you are!
public partial class App : Application
{
// ##############################################################################################################################
// Constructor
// ##############################################################################################################################
#region Constructor
static App()
{
// set MyInitialized=true for new windows (happens before Loaded)
EventManager.RegisterClassHandler(typeof(Window), FrameworkElement.SizeChangedEvent, new RoutedEventHandler(OnSizeChanged));
// our loaded handler
EventManager.RegisterClassHandler(typeof(UIElement), FrameworkElement.LoadedEvent, new RoutedEventHandler(OnLoaded), true);
EventManager.RegisterClassHandler(typeof(ContentElement), FrameworkContentElement.LoadedEvent, new RoutedEventHandler(OnLoaded), true);
}
private static void OnSizeChanged(object sender, RoutedEventArgs e)
{
//Console.WriteLine("SizeChanged {0}", sender);
SetMyInitialized((Window) sender, true);
}
private static void OnLoaded(object sender, RoutedEventArgs e)
{
Trace.WriteLine($"{DateTime.Now:O}: {sender} loaded");
}
#endregion
// ##############################################################################################################################
// MyInitialized
// ##############################################################################################################################
#region MyInitialized
public static void SetMyInitialized(UIElement element, bool value)
{
element.SetValue(MyInitializedProperty, value);
}
public static bool GetMyInitialized(UIElement element)
{
return (bool) element.GetValue(MyInitializedProperty);
}
public static readonly DependencyProperty MyInitializedProperty = DependencyProperty.RegisterAttached("MyInitialized", typeof (bool), typeof (App), new FrameworkPropertyMetadata(false, FrameworkPropertyMetadataOptions.Inherits, OnMyInitializedChanged));
private static void OnMyInitializedChanged(DependencyObject dpo, DependencyPropertyChangedEventArgs ev)
{
if ((bool)ev.NewValue)
{
// registering instance handler unbreaks class handlers
if (dpo is FrameworkElement element)
element.Loaded += _EmptyRoutedEventHandler;
if (dpo is FrameworkContentElement contentElement)
contentElement.Loaded += _EmptyRoutedEventHandler;
} else
{
throw new ArgumentException("Cannot set to false", ev.Property.Name);
}
//Console.WriteLine("MyInitialized {0} {1}=>{2}", dpo, ev.OldValue, ev.NewValue);
}
private static readonly RoutedEventHandler _EmptyRoutedEventHandler = delegate { };
#endregion
}
XAML
<Window x:Class="WpfApp3.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:WpfApp3"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800"
d:DataContext="{d:DesignInstance local:MainWindow}">
<Grid>
<Border Background="Green" VerticalAlignment="Center" HorizontalAlignment="Center" Width="30" Height="20">
<TextBlock Background="Orange" Text="hello"></TextBlock>
</Border>
</Grid>
</Window>
Sample Console output:
2018-07-31T14:20:52.6052225+02:00: WpfApp3.MainWindow loaded
2018-07-31T14:20:52.6112064+02:00: System.Windows.Controls.Border loaded
2018-07-31T14:20:52.6132008+02:00: System.Windows.Documents.AdornerDecorator loaded
2018-07-31T14:20:52.6141984+02:00: System.Windows.Controls.ContentPresenter loaded
2018-07-31T14:20:52.6141984+02:00: System.Windows.Controls.Grid loaded
2018-07-31T14:20:52.6151966+02:00: System.Windows.Controls.Border loaded
2018-07-31T14:20:52.6161935+02:00: System.Windows.Controls.TextBlock loaded
2018-07-31T14:20:52.6161935+02:00: System.Windows.Documents.AdornerLayer loaded

Related

AssociatedObject.DataContext is null in Behavior in ContentPresenter

I try to use a Behavior to trigger a method in the View from the VM. To this end, I use an object of type Trigger with an Event and invoker in the VM, and bind this to a dependency property in the behavior. The behavior does the subscription to the event in its Loaded callback. This works as expected, when the event in the VM is invoked, I can call the method in the view using AssociatedObject.
However, when the Behavior is inside a DataTemplate for a ContentPresenter, I see the following weird behavior (no pun intended...). Given a ContentPresenter with two DataTemplates, used depending on the type of Content (Tab1 or Tab2), when the Content is changed from Tab1 to Tab2, everything is fine. When it is changed from one instance of Tab1 to another instance of Tab1, however, AssociatedObject.DataContext is suddently null (and the method I try to call on the View fails due to this).
I tried to create a minimal example to demonstrate this. The code below should have everything to just run in a new WPF app. Observe the debug output in the following click paths to reproduce:
Tab -> Tab1 -> Invoke -> Output as expected
Tab -> Tab1a -> Tab1 -> Invoke -> DataContext is empty
Tab -> Tab1 -> Again as expected
I can think of ways to work around this problem, but I would like to understand it. I assume it is related to the DataTemplate not being rebuilt when the Content changes to the same type, but still I would expect AssociatedObject to point to the correct Grid (which I think it does not, since the DataContext in the actually shown Grid is fine). Any ideas are highly appreciated!
MainWindow.xaml
<Window x:Class="EmptyWpfApp.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:i="http://schemas.microsoft.com/expression/2010/interactivity"
xmlns:local="clr-namespace:EmptyWpfApp"
mc:Ignorable="d"
Title="MainWindow" Height="350" Width="525">
<Window.DataContext>
<local:ViewModel />
</Window.DataContext>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>
<ContentPresenter Grid.Row="0" Content="{Binding Tab}">
<ContentPresenter.Resources>
<DataTemplate DataType="{x:Type local:Tab1}">
<Grid>
<i:Interaction.Behaviors>
<local:TestBehavior Trigger="{Binding T}"/>
</i:Interaction.Behaviors>
<TextBlock>With behavior</TextBlock>
</Grid>
</DataTemplate>
<DataTemplate DataType="{x:Type local:Tab2}">
<Grid>
<TextBlock>Empty</TextBlock>
</Grid>
</DataTemplate>
</ContentPresenter.Resources>
</ContentPresenter>
<Button Grid.Row="1" Click="ButtonBase_OnClick">Tab</Button>
<Button Grid.Row="1" Grid.Column="1" Click="ButtonBase_OnClick1">Tab1</Button>
<Button Grid.Row="1" Grid.Column="2" Click="ButtonBase_OnClick1a">Tab1a</Button>
<Button Grid.Row="2" Grid.ColumnSpan="3" Click="ButtonBase_OnClick2">Invoke on VM</Button>
</Grid>
MainWindows.xaml.cs:
using System;
using System.ComponentModel;
using System.Diagnostics;
using System.Runtime.CompilerServices;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Interactivity;
using EmptyWpfApp.Annotations;
namespace EmptyWpfApp
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
private Tab2 _tab = new Tab2();
private Tab1 _tab1 = new Tab1();
private Tab1 _tab1a = new Tab1();
public MainWindow()
{
InitializeComponent();
DataContext = new ViewModel();
}
private void ButtonBase_OnClick(object sender, RoutedEventArgs e)
{
((ViewModel)DataContext).Tab = _tab;
}
private void ButtonBase_OnClick1(object sender, RoutedEventArgs e)
{
((ViewModel)DataContext).Tab = _tab1;
}
private void ButtonBase_OnClick1a(object sender, RoutedEventArgs e)
{
((ViewModel)DataContext).Tab = _tab1a;
}
private void ButtonBase_OnClick2(object sender, RoutedEventArgs e)
{
(((ViewModel) DataContext).Tab as Tab1)?.T.OnE();
}
}
public class ViewModel : INotifyPropertyChanged
{
private ITab _tab;
public ITab Tab
{
get => _tab;
set
{
_tab = value;
OnPropertyChanged();
}
}
public event PropertyChangedEventHandler PropertyChanged;
[NotifyPropertyChangedInvocator]
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
public interface ITab {}
public class Tab1 : ITab
{
public Trigger T { get; } = new Trigger();
}
public class Tab2 : ITab {}
public class Trigger
{
public event EventHandler E;
public virtual void OnE()
{
E?.Invoke(this, EventArgs.Empty);
}
}
public class TestBehavior : Behavior<Grid>
{
public static readonly DependencyProperty TriggerProperty = DependencyProperty.Register(
"Trigger",
typeof(Trigger),
typeof(TestBehavior),
new PropertyMetadata(default(Trigger)));
public Trigger Trigger {
get => (Trigger)GetValue(TriggerProperty);
set => SetValue(TriggerProperty, value);
}
protected override void OnAttached()
{
base.OnAttached();
AssociatedObject.Loaded += OnLoaded;
AssociatedObject.Unloaded += Cleanup;
}
private void Cleanup(object sender, RoutedEventArgs e)
{
Cleanup();
}
protected override void OnDetaching()
{
base.OnDetaching();
Cleanup();
}
private void Cleanup()
{
AssociatedObject.Loaded -= OnLoaded;
if (Trigger != null)
Trigger.E -= TriggerOnE;
}
private void OnLoaded(object sender, RoutedEventArgs e)
{
Subscribe();
}
private void Subscribe()
{
Trigger.E += TriggerOnE;
}
private void TriggerOnE(object sender, EventArgs e)
{
Debug.WriteLine("DC:" + AssociatedObject.DataContext);
}
}
}

Dependency property not working on a interaction behavior

I am trying to add a dependency property IsActive to a RubberBandBehavior I found on CodeProject, so that I can activate and deactivate it from my ViewModel. The code below does not give any compile error and runs, but the value does not seem to be set correctly, when I check the line with the comment \\ this line is always 'false'.
The modified class RubberBandBehavior:
public class RubberBandBehavior : Behavior<ListBox>
{
public static readonly DependencyProperty IsActiveProperty =
DependencyProperty.Register("IsActive", typeof(bool), typeof(RubberBandBehavior),
new PropertyMetadata(IsActiveProperty_Changed));
private static void IsActiveProperty_Changed(DependencyObject sender,
DependencyPropertyChangedEventArgs args)
{
// This gets called _after_ OnAttached!
}
public bool IsActive
{
get { return (bool)GetValue(IsActiveProperty); }
set { SetValue(IsActiveProperty, value); }
}
protected override void OnAttached()
{
AssociatedObject.Loaded += new System.Windows.RoutedEventHandler(AssociatedObject_Loaded);
base.OnAttached();
}
void AssociatedObject_Loaded(object sender, System.Windows.RoutedEventArgs e)
{
bool a = IsActive; // this line is always 'false'
RubberBandAdorner band = new RubberBandAdorner(AssociatedObject);
AdornerLayer adornerLayer = AdornerLayer.GetAdornerLayer(AssociatedObject);
adornerLayer.Add(band);
}
protected override void OnDetaching()
{
AssociatedObject.Loaded -= new System.Windows.RoutedEventHandler(AssociatedObject_Loaded);
base.OnDetaching();
}
}
In my XAML I have this:
<i:Interaction.Behaviors>
<behavior:RubberBandBehavior IsActive="True"/>
</i:Interaction.Behaviors>
My plan is then to bind to my ViewModel like this, but first I need to get the above sorted out:
<i:Interaction.Behaviors>
<behavior:RubberBandBehavior IsActive="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type UserControl}}, Path=DataContext.IsEditable}"/>
</i:Interaction.Behaviors>
You wont get this value in the AssociatedObject.Loaded event. You need to use the IsActiveProperty_Changed handler in your code example to get the correct value of this property

Got focus Event to handle all textbox

This is my coding:
public partial class App : Application
{
protected override void OnStartup(StartupEventArgs e)
{
EventManager.RegisterClassHandler(typeof(TextBox), UIElement.PreviewMouseLeftButtonDownEvent,
new MouseButtonEventHandler(SelectivelyHandleMouseButton), true);
EventManager.RegisterClassHandler(typeof(TextBox), UIElement.GotKeyboardFocusEvent,
new RoutedEventHandler(SelectAllText), true);
base.OnStartup(e);
}
private static void SelectivelyHandleMouseButton(object sender, MouseButtonEventArgs e)
{
var textbox = (sender as TextBox);
if (textbox != null && !textbox.IsKeyboardFocusWithin)
{
if( e.OriginalSource.GetType().Name == "TextBoxView" )
{
e.Handled = true;
textbox.Focus();
}
}
}
I got error in:
onstartup() method- method cannot be override
In App.xaml you need to subscribe to event Startup:
<Application x:Class="WpfApplication1.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
StartupUri="MainWindow.xaml" Startup="Application_Startup">
<Application.Resources>
</Application.Resources>
</Application>
In App.xaml.cs:
private void Application_Startup(object sender, StartupEventArgs e)
{
// Your code here
}

Close Window without code-behind in WPF

Is it possible to bind a Button to Close the Window without adding a code-behind event?
<Button Content="OK" Command="{Binding CloseWithSomeKindOfTrick}" />
Instead of the following XAML:
<Button Content="OK" Margin="0,8,0,0" Click="Button_Click">
With the code-behind:
private void Button_Click(object sender, RoutedEventArgs e)
{
Close();
}
Thanks!
If you want close the dialog Window, you can add for Button IsCancel property:
<Button Name="CloseButton"
IsCancel="True" ... />
This means the following MSDN:
When you set the IsCancel property of a Button to true, you create a Button that is registered with the AccessKeyManager. The button is then activated when a user presses the ESC key.
Now, if you click on this Button, or press Esc then dialog Window is closing, but it does not work for the normal MainWindow.
To close the MainWindow, you can simply add a Click handler which has already been shown. But if you want a more elegant solution that would satisfy the MVVM style you can add the attached behavior:
public static class ButtonBehavior
{
#region Private Section
private static Window MainWindow = Application.Current.MainWindow;
#endregion
#region IsCloseProperty
public static readonly DependencyProperty IsCloseProperty;
public static void SetIsClose(DependencyObject DepObject, bool value)
{
DepObject.SetValue(IsCloseProperty, value);
}
public static bool GetIsClose(DependencyObject DepObject)
{
return (bool)DepObject.GetValue(IsCloseProperty);
}
static ButtonBehavior()
{
IsCloseProperty = DependencyProperty.RegisterAttached("IsClose",
typeof(bool),
typeof(ButtonBehavior),
new UIPropertyMetadata(false, IsCloseTurn));
}
#endregion
private static void IsCloseTurn(DependencyObject sender, DependencyPropertyChangedEventArgs e)
{
if (e.NewValue is bool && ((bool)e.NewValue) == true)
{
if (MainWindow != null)
MainWindow.PreviewKeyDown += new KeyEventHandler(MainWindow_PreviewKeyDown);
var button = sender as Button;
if (button != null)
button.Click += new RoutedEventHandler(button_Click);
}
}
private static void button_Click(object sender, RoutedEventArgs e)
{
MainWindow.Close();
}
private static void MainWindow_PreviewKeyDown(object sender, KeyEventArgs e)
{
if (e.Key == Key.Escape)
MainWindow.Close();
}
}
And in MainWindow use this Behavior like as:
<Window x:Class="MyProjectNamespace.MainWindow"
xmlns:local="clr-namespace:MyProjectNamespace">
<Button Name="CloseButton"
local:ButtonBehavior.IsClose="True" ... />

dynamically assigning an event

I am dynamically creating a GroupBox and trying to assign the MouseLeftButtonDown event to it to perform some action when the user left-clicks on it. This is what I've tried:
public MyClass()
{
tagGroupBox.MouseLeftButtonDown += new MouseButtonEventHandler(tagGroupBox_MouseLeftButtonDown); //generates error: "tagGroupBox_MouseLeftButtonDown does not exist in the current context"
}
private void tagGroupBox__MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
MessageBox.Show("Left click event triggered");
}
There are __ (double underscores) in handler method.
void tagGroupBox_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
}
This works for me:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
GroupBox g = new GroupBox();
g.MouseLeftButtonUp += new MouseButtonEventHandler(g_MouseLeftButtonUp);
MainGrid.Children.Add(g);
}
void g_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
{
System.Diagnostics.Debugger.Break();
}
}
XAML
<Window x:Class="WpfApplication1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">
<Grid x:Name="MainGrid">
</Grid>
</Window>

Categories

Resources