How can I control the loading sequence of controls? - c#

My English skill is poor because I'm not a native English speaker.
My application has a MainWindowView and MainWindowBehavior also MainWindowView has control(Editor) as the following code.
<MainWindowView>
<Grid>
<TabControl>
<TabItem>
<Grid>
<Editor x:Name="editor"/>
</Grid>
</TabItem>
</TabControl>
</Grid>
<i:Interaction.Behaviors>
<behaviors:MainWindowBehavior/>
</i:Interaction.Behaviors>
</MainWindowView>
MainWindowBehavior uses the property of Editor in the LoadedEventHandler of MainWindowView.
The following code shows the above logic.
protected override void OnDetaching()
{
this.AssociatedObject.Loaded -= AssociatedObject_Loaded;
base.OnDetaching();
}
protected override void OnAttached()
{
base.OnAttached();
this.AssociatedObject.Loaded += AssociatedObject_Loaded;
}
private void AssociatedObject_Loaded(object sender, RoutedEventArgs e)
{
this.mainWindow = sender as MainWindow;
// run time error
this.mainWindow.editor.Parser.ParsingFailed += Parser_ParsingFailed;
}
But compiler shows run time error because of the value of the Parser property of the editor is null.
I tried to initialize the parser property of the editor at the Constructer, OnApplyTemplate function, Loaded EventHandler but 3 cases all called late than Loaded EventHandler of MainWindow.
And as a result, generate run time error.
I think that the Loaded EventHandler of the editor must be called early more Loaded EventHandler of the MainWindowBehavior. But in fact, the sequence reverse.
I don't know why the sequence reverse.
How can I change the loading sequence as I thought?
Thank you for reading.

Maybe you cannot change the sequence of events, but you can sure change the way you listen to these events. I suggest you to hook-up to an event in your mainWindow that will indicate you when its editor property is set.
Your code would become:
private void AssociatedObject_Loaded(object sender, RoutedEventArgs e)
{
this.mainWindow = sender as MainWindow;
// Here we don't access mainWindow.editor anymore, we hook-up to an event instead
this.mainWindow.OnEditorPropertyChanged += MainWindow_EditorPropertyChanged;
}
private void MainWindow_EditorPropertyChanged(object sender){
{
var mainWindow = sender as MainWindow;
if (mainWindow.editor != null) {
mainWindow.editor.Parser.ParsingFailed += Parser_ParsingFailed;
}
}
And in your MainWindow, make sure to raise an event when its editor property is set, for example:
public delegate void OnEditorPropertyChangedEventHandler(object sender);
public event OnEditorPropertyChangedEventHandler OnEditorPropertyChanged;
// Backing field
private Editor _editor;
public Editor editor {
get => _editor;
set => {
_editor = value;
OnEditorPropertyChanged?.Invoke(this); // Raise the event
}
}

I found the cause of my problem while following your advice.
I think the OnApplyTemplate function of the Editor is called early than the Loaded event of the MainWindowView. But, in fact, the OnApplyTemplate function of the Editor is not called.
I have been apprehending wrong about my problem. I'm sorry...
Now I would make my problem right.
The Editor is Custom-Control. I missed adding code in the Generic.xaml file the following code.
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="/WpfControls;component/Themes/Editor.xaml"/>
</ResourceDictionary.MergedDictionaries>
Now I added the above code in the Generic.xaml and the OnApplyTemplate function of Editor is called normally also it is called early than the Loaded event of the MainWindowView.
Thank you for your help and interest in my problem.

Related

How to pass shortcuts from MainWindow to the UserControl in ContentControl?

The UserControl dynamically loaded to the ContentControl in my Window does not receive keyboard shortcuts defined inside of UserControl XAML, in case it is not focused.
I need to implement keyboard shortcuts for dynamically loaded UserControl, without focusing the UserControl.
I cannot define InputBindings on MainWindow, because the InputBindigs are changing depends on currently loaded UserControl.
1) So I tried to send all Window_KeyUp to the loaded UserControl via RaiseEvent, no luck. (StackOverflow Exception or no action called)
2) I tried also fillup the MainWindow.InputBindings by LoadedUserControl.InputBindings when the UserControl has been loaded to the ContentControl... no luck (defined command is unknown in context)
UserControl.xaml
----------------
<UserControl.InputBindings>
<KeyBinding Key="N" Command="{Binding Path=NewOrderCommand}" Modifiers="Control" />
</UserControl.InputBindings>
This is working if UserControl is focused.
So to get rid of focusing I tried this:
MainWindow.xaml.cs
-------------------
private void MainWindow_KeyUp(object sender, System.Windows.Input.KeyEventArgs e)
{
// ModulePanel points to the loaded UserControl
ViewModel.CurrentModule.ModulePanel.RaiseEvent(e);
e.Handled = true;
}
So this issued StackOverflowException
I tried set e.Handled = true; before RaiseEvent(e) but it does not pass the event to the UserControl - so nothing happens;
I also tried to get InputBindings from UserControl to MainWindow:
foreach(InputBinding bi in UserControl.InputBindings)
MainWindow.InputBindings.Add(bi);
But I got exception in Debug window:
Cannot find governing FrameworkElement or FrameworkContentElement for target element. BindingExpression:Path=NewOrderCommand; DataItem=null; target element is 'KeyBinding' (HashCode=35527846); target property is 'Command' (type 'ICommand')
My expectation is, that I will dynamically change Window InputBindings depends on loaded UserControl, where the InputBindings are defined.
If you want the UserControl to be able to handle all key presses in the window, you could get a reference to the parent window using the Window.GetWindow once the UserControl has been loaded and then hook up an event handler to it:
public partial class UserControl1 : UserControl
{
public UserControl1()
{
InitializeComponent();
Loaded += (s, e) =>
{
Window parentWindow = Window.GetWindow(this);
parentWindow.PreviewKeyDown += ParentWindow_PreviewKeyDown;
};
Unloaded += (s, e) =>
{
Window parentWindow = Window.GetWindow(this);
parentWindow.PreviewKeyDown -= ParentWindow_PreviewKeyDown;
};
}
private void ParentWindow_PreviewKeyDown(object sender, KeyEventArgs e)
{
//do something...
}
}
Try using ViewModels as DataContext and let them communicate with each other using a Messenger. You can send the Information needed to the ViewModel of the unfocused Usercontrol.
If you want to stick with code-behind, subscribe to the eventhandler on Mainwindow:
PreviewKeyUp += userCtrl.OnKeyUp;
and handle it in your UserControl
public void OnKeyUp(object sender, KeyEventArgs e)
{
MyTextBlock.Text = e.Key.ToString();
}

Why my custom event fires twice?

I've searched relevant posts but I got nothing much
I have created a user control. In my user control there is a text box. I want to have an event in my user control that fires whenever text box TextChanged event raises. This is what I have done so far : (This is code of user control)
public event EventHandler txtchnged;
public void ontxtchnged()
{
txtchnged(this, EventArgs.Empty);
}
public MyTextBox()
{
InitializeComponent();
textBox1.TextChanged += textBox1_TextChanged;
}
private void textBox1_TextChanged(object sender, EventArgs e)
{
ontxtchnged();
}
Here is where I have used user control
public RegisterMainFrm()
{
InitializeComponent();
myUserControl1.txtchnged += myUserControl1_txtchnged;
}
private void myUserControl1_txtchnged(object sender, EventArgs e)
{
Console.WriteLine("hello");
}
This works and I know that the code might not be clean but that's not the problem. Problem is : "hello" will be printed in console twice and I really don't know why and how to fix it.
From MSDN on TextBox.TextChanged:
Note:This event fires when the TextBox control is created and
initially populated with text.
Could this be your problem that you get the initial event?
UPDATE:
From Adriano Repetti Hint in Comments: Did you get the textBox1_TextChanged event handler by double clicking in the designer?
Then you have added a second hook to the TextChanged Event.
Check the code inside InitializeComponent of your UserControl if it is already hooking the event.

Raise Textblock Tapped event manually

I have TextBlock I have declared it in Xaml with its Tapped event.Now i wanted to raise this event manually from code without actually Tap on it from external input.
private void TxtBlkMessages_Tapped_1(object sender, TappedRoutedEventArgs e)
{
// want to raise it manually
// some storyboards animation is present in it
}
I have defined it in xaml like this..
<TextBlock Name="TxtBlkMessages" Tapped="TxtBlkMessages_Tapped_1" />
More precisely i want to raise it from viewmodel on basis of some condition.
If you want it to raise manually then simply u can call the method
Example :
private void CallManually()
{
TxtBlkMessages_Tapped_1(null, null);
}
Just call:
TxtBlkMessages.RaiseEvent(new TappedRoutedEventArgs());

Help with c# event listening and usercontrols

OK so I have a page which has a listview on it. Inside the item template of the listview is a usercontrol. This usercontrol is trying to trigger an event so that the hosting page can listen to it. My problem is that the event is not being triggered as the handler is null. (ie. EditDateRateSelected is my handler and its null when debugging)
protected void lnkEditDate_Click(object sender, EventArgs e)
{
if (EditDateRateSelected != null)
EditDateRateSelected(Convert.ToDateTime(((LinkButton)frmViewRatesDate.Row.FindControl("lnkEditDate")).Text));
}
On the item data bound of my listvew is where I'm adding my event handlers
protected void PropertyAccommodationRates1_ItemDataBound(object sender, ListViewItemEventArgs e)
{
if (e.Item.ItemType == ListViewItemType.DataItem)
{
UserControls_RatesEditDate RatesViewDate1 = (UserControls_RatesEditDate)e.Item.FindControl("RatesViewDate1");
RatesViewDate1.EditDateRateSelected += new UserControls_RatesEditDate.EditDateRateEventHandler(RatesEditDate1_EditDateRateSelected);
RatesViewDate1.PropertyID = (int)Master.PropertyId;
if (!String.IsNullOrEmpty(Accommodations1.SelectedValue))
{
RatesViewDate1.AccommodationTypeID = Convert.ToInt32(Accommodations1.SelectedValue);
}
else
{
RatesViewDate1.AccommodationTypeID = 0;
}
RatesViewDate1.Rate = (PropertyCMSRate)((ListViewDataItem)e.Item).DataItem;
}
}
My event code all works fine if the control is inside the page and on page load I have the line:
RatesEditDate1.EditDateRateSelected += new UserControls_RatesEditDate.EditDateRateEventHandler(RatesEditDate1_EditDateRateSelected);
But obviously I need listen for events inside the listviewcontrols.
Any advice would be greatly appreciated. I have tried setting EnableViewState to true for my listview but that hasn't made a difference. Is there somewhere else I'm supposed to be wiring up the control handler?
Note - apologies if I've got my terminology wrong and I'm referring to delegates as handlers and such :)
OK I tried wiring the event to my user control in the source like so:
<uc1:RatesEditDate ID="RatesViewDate1" runat="server" OnEditDateRateSelected="RatesEditDate1_EditDateRateSelected" />
and then found it complaining about it being inaccessible due to its protection level. Inside my usercontrol though - I've made my delegate and event public ???
public delegate void EditDateRateEventHandler(DateTime theDateTime);
public event EditDateRateEventHandler EditDateRateSelected;
Turns out that within my hosting page - my event handler had no accessiblity defined on it (so was private) - so it needed to be made protected and hey presto was happy!
Problem solved!

Is there a "All Children loaded" event in WPF

I am listening for the loaded event of a Page. That event fires first and then all the children fire their load event. I need an event that fires when ALL the children have loaded. Does that exist?
I hear you. I also am missing an out of the box solution in WPF for this.
Sometimes you want some code to be executed after all the child controls are loaded.
Put this in the constructor of the parent control
Dispatcher.BeginInvoke(DispatcherPriority.Loaded, new Action(() => {code that should be executed after all children are loaded} ));
Helped me a few times till now.
Loaded is the event that fires after all children have been Initialized. There is no AfterLoad event as far as I know. If you can, move the children's logic to the Initialized event, and then Loaded will occur after they have all been initialized.
See MSDN - Object Lifetime Events.
You can also use the event: ContentRendered.
http://msdn.microsoft.com/en-us/library/ms748948.aspx#Window_Lifetime_Events
WPF cant provide that kind of an event since most of the time Data is determining whther to load a particular child to the VisualTree or not (for example UI elements inside a DataTemplate)
So if you can explain your scenario little more clearly we can find a solution specific to that.
One of the options (when content rendered):
this.LayoutUpdated += OnLayoutUpdated;
private void OnLayoutUpdated(object sender, EventArgs e)
{
if (!isInitialized && this.ActualWidth != 0 && this.ActualHeight != 0)
{
isInitialized = true;
// Logic here
}
};
Put inside your xaml component you want to wait for, a load event Loaded="MyControl_Loaded" like
<Grid Name="Main" Loaded="Grid_Loaded"...>
<TabControl Loaded="TabControl_Loaded"...>
<MyControl Loaded="MyControl_Loaded"...>
...
and in your code
bool isLoaded;
private void MyControl_Loaded(object sender, RoutedEventArgs e)
{
isLoaded = true;
}
Then, inside the Event triggers that have to do something but were triggering before having all components properly loaded, put if(!isLoaded) return; like
private void OnButtonChanged(object sender, RoutedEventArgs e)
{
if(!isLoaded) return;
... // code that must execute on trigger BUT after load
}
I ended up doing something along these lines.. your milage may vary.
void WaitForTheKids(Action OnLoaded)
{
// After your children have been added just wait for the Loaded
// event to fire for all of them, then call the OnLoaded delegate
foreach (ContentControl child in Canvas.Children)
{
child.Tag = OnLoaded; // Called after children have loaded
child.Loaded += new RoutedEventHandler(child_Loaded);
}
}
internal void child_Loaded(object sender, RoutedEventArgs e)
{
var cc = sender as ContentControl;
cc.Loaded -= new RoutedEventHandler(child_Loaded);
foreach (ContentControl ctl in Canvas.Children)
{
if (!ctl.IsLoaded)
{
return;
}
}
((Action)cc.Tag)();
}

Categories