Attach to a click event in XAML control when loading XAML dynamically. - c#

I have a XAML control which gets loaded dynamically at runtime. This is pure XAML with no code behind.
I dont have any control over the parent loading mechanism which is why this looks a bit weird.
I have a parent application which loads my Plugin DLL and loads my XAML Control.
My DLL signature is:
public class Application : BaseClassHere
{
public Application(IParentContext context) : base(context)
{
// Im placing this instance in the bag which i use in my XAML
base.MyObservablePropertyBag["MyParentContext"] = new ObservableValue<object>(this);
}
}
My XAML is like this:
<av:UserControl
xmlns:local="clr-namespace:MyApplicationNS;assembly=MyDll"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:av="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008" >
<Border DataContext="{Binding MyObservablePropertyBag[MyParentContext].Value}" >
<Button Name="MyButton" />
</Border>
</UserControl>
This binding works perfectly in the XAML. I have full access to all public properties that i definein the Application class.
My problem is that i want to link to up Click events on my Buttons. If i add a Click event in the XAML it errors at runtime telling me that i need to compile my XAML.
Is there any way to subscribe to the Click event on MyButton in the Application class?

Well, if you say binding works then why try to mess with the Click event? Rather go with the Button's Command property. Create an ICommand instance (like a DelegateCommand or RelayCommand) that you place in your propertybag and just bind to it!

Related

Dynamic controls switching in WPF

Title might be misleading but i'm not sure how to describe it.
Lets say i have 2 containers - one on the left, one on the right. Left container has multiple buttons. Pressing them will change whats inside 2nd container.
If i press 1st button a set of buttons and calendar will appear, 2nd - datagridview etc. Its example.
How can i achieve it? I'm not asking for solution (it can't be solved in one line of code, obviously), but what should i search for. Some specific control? Displaying other window inside it? Etc.
I am not sure if I understood the question well, so I wrote the following scenario from what I understood.
As you mentioned, you have a main window that contains 2 panels, one on the left and the other on the right. In the left panel, there is a list of buttons placed as a group of menus, which, when clicked, show other content in the right panel, something like a navigation to another system module (see the gif):
If this is your scenario, you can design your WPF application as follows:
Create UserControls for each screen you want to navigate to. In the previous example, you could create a UserControl for the module of the task list, and another UserControl for the module of My Agenda. Check this link so you know what a UserControl is.
Manage navigation on the main window. Just like in WinForms, you could handle the click event on each button in the left panel, however, an elegant way to handle the click event is that your handle it in the parent container, since, unlike Winforms, the click event is a bubbling event. Check this link, so you know what a routed event and what is a bubbling event.
In the example video, could you notice that each module is in a container that has a header and that the header text changes when the button is clicked and the header text is updated with the button text? This can be done in many ways, but a good way to do it is through data binding, check this link to understand what this concept is. With experience, you will realize when it will be advisable to apply this and when it will not.
As you can see, there are many concepts that you should review and learn to be able to make a good design of an application taking advantage of all the benefits that WPF has and to continue with the philosophy of WPF.
I write an example code that I also publish on GitHub. I explain some things about the code, but I suggest that you expand these concepts in the links that I left you and in other reliable sources of knowledge, such as books or tutorials from Microsoft itself.
The Xaml MainWindow:
<Window
x:Class="WpfApp26.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:local="clr-namespace:WpfApp26"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
Title="MainWindow"
Width="800" Height="450"
d:DataContext="{d:DesignInstance Type=local:ViewModel}"
mc:Ignorable="d">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="2*" />
</Grid.ColumnDefinitions>
<!-- A GroupBox is a control with a header -->
<GroupBox Header="Options">
<!-- Look that the click event is handled in the StackPanel, the container for the buttons -->
<StackPanel Button.Click="ModuleSelected_OnClick">
<Button
Margin="5" Padding="5"
Content="To Do List" Tag="ToDoListModule" />
<Button
Margin="5" Padding="5"
Content="My Agenda" Tag="MyAgendaModule" />
</StackPanel>
</GroupBox>
<!-- The header property is binding to the CurrentModuleName property in the DataContext -->
<GroupBox Name="GbCurrentModule" Grid.Column="1" Header="{Binding CurretModuleName}" />
</Grid>
</Window>
The MainWindow code behind (review the INotifyProperyChanged):
public partial class MainWindow : Window {
private readonly ViewModel vm;
public MainWindow() {
InitializeComponent();
// Setting the Window's DataContext to a object of the ViewModel class.
this.DataContext = this.vm = new ViewModel();
}
private void ModuleSelected_OnClick(object sender, RoutedEventArgs e) {
// The Source property of the RoutedEventArgs gets the Element that fires the event (in this case, the button).
var clickedButton = (Button) e.Source;
this.vm.CurretModuleName = clickedButton.Content.ToString();
// Getting the Tag property of the button.
var tag = clickedButton.Tag.ToString();
// Performing the navigation.
switch (tag) {
case "ToDoListModule":
NavigateToModule(new UcToDoListModule());
break;
case "MyAgendaModule":
NavigateToModule(new UcMyAgendaModule());
break;
}
#region Internal methods
void NavigateToModule(UserControl uc) {
this.GbCurrentModule.Content = uc;
}
#endregion
}
}
The ViewModel class:
// The class implementents the INotifyPropertyChanged interface, that is used
// by the WPF notifications system.
public class ViewModel : INotifyPropertyChanged {
private string curretModuleName;
public string CurretModuleName {
get => this.curretModuleName;
set {
this.curretModuleName = value;
this.OnPropertyChanged();
}
}
#region INotifyPropertyChanged
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null) {
this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
#endregion
}
You can use DataTemplates with Data Binding: https://learn.microsoft.com/en-us/dotnet/framework/wpf/data/data-templating-overview
This will allow you to define templates that are automatically applied to objects of specific types. So you could have a calendar object, list view, data grid, etc apply individually.
You could also use the visibility to show/hide the view as desired when your button(s) are clicked.
MVVM frameworks use this quite often: https://compiledexperience.com/blog/posts/using-caliburn-micro-as-a-data-template-selector
Another example https://www.codemag.com/article/0907111/Dressing-Up-Your-Data-with-WPF-DataTemplates
There are also other MVVM approaches that use activators to show/hide/generate new objects of specific types and display them.

WPF Resource file attached to Style file: Error -> The name 'InitializeComponent' does not exist in the current context [duplicate]

Is it possible to set code behind a resource dictionary in WPF. For example in a usercontrol for a button you declare it in XAML. The event handling code for the button click is done in the code file behind the control. If I was to create a data template with a button how can I write the event handler code for it's button click within the resource dictionary.
I think what you're asking is you want a code-behind file for a ResourceDictionary. You can totally do this! In fact, you do it the same way as for a Window:
Say you have a ResourceDictionary called MyResourceDictionary. In your MyResourceDictionary.xaml file, put the x:Class attribute in the root element, like so:
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="MyCompany.MyProject.MyResourceDictionary"
x:ClassModifier="public">
Then, create a code behind file called MyResourceDictionary.xaml.cs with the following declaration:
namespace MyCompany.MyProject
{
partial class MyResourceDictionary : ResourceDictionary
{
public MyResourceDictionary()
{
InitializeComponent();
}
... // event handlers ahead..
}
}
And you're done. You can put whatever you wish in the code behind: methods, properties and event handlers.
== Update for Windows 10 apps ==
And just in case you are playing with UWP there is one more thing to be aware of:
<Application x:Class="SampleProject.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:rd="using:MyCompany.MyProject">
<!-- no need in x:ClassModifier="public" in the header above -->
<Application.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<!-- This will NOT work -->
<!-- <ResourceDictionary Source="/MyResourceDictionary.xaml" />-->
<!-- Create instance of your custom dictionary instead of the above source reference -->
<rd:MyResourceDictionary />
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Application.Resources>
</Application>
I disagree with "ageektrapped"... using the method of a partial class is not a good practice. What would be the purpose of separating the Dictionary from the page then?
From a code-behind, you can access a x:Name element by using:
Button myButton = this.GetTemplateChild("ButtonName") as Button;
if(myButton != null){
...
}
You can do this in the OnApplyTemplate method if you want to hookup to controls when your custom control loads. OnApplyTemplate needs to be overridden to do this. This is a common practice and allows your style to stay disconnected from the control. (The style should not depend on the control, but the control should depend on having a style).
Gishu - whilst this might seem to be a "generally not to be encouraged practice" Here is one reason you might want to do it:
The standard behaviour for text boxes when they get focus is for the caret to be placed at the same position that it was when the control lost focus. If you would prefer throughout your application that when the user tabs to any textbox that the whole content of the textbox was highlighted then adding a simple handler in the resource dictionary would do the trick.
Any other reason where you want the default user interaction behaviour to be different from the out of the box behaviour seems like good candidates for a code behind in a resource dictionary.
Totally agree that anything which is application functionality specific ought not be in a code behind of a resource dictionary.
Adding on....these days, with the advent of {x:Bind ...}, if you want to put your DataTemplate into a shared ResourceDictionary file, you are required to give that file a code behind.
XAML is for constructing object graphs not containing code.
A Data template is used to indicate how a custom user-object is to be rendered on screen... (e.g. if it is a listbox item) behavior is not part of a data template's area of expertise. Redraw the solution...

UWP xaml binding command to grid onfocus

In a UWP Windows 10 application I'm working on have a command in my Viewmodel
public class ViewModel {
public ICommand LoadedCommand{ get; set; }
}
and I'd like to run it when my grid control gets the focus, without having to call the command in the code behind file.
<Grid GotFocus="Grid_GotFocus">
<Grid.ColumnDefinitions>
</Grid.ColumnDefinitions>
</Grid>
Does anyone know if its possible to assign the command to the event directly in the xaml and if it is how I might do it.
Many Thanks
You can use either the InvokeCommandAction from the XAML Behavior library, or use x:Bind to event that's available in the Anniversary Update. So you can have something like -
GotFocus="{x:Bind Vm.OnGridGotFocus}"
Note the OnGridGotFocus() here is a method and it can either have no parameters or matdch the signiture of the event.

Using a command on a custom control

I'm trying to use the search text box (which I made by following this tutorial: http://davidowens.wordpress.com/2009/02/18/wpf-search-text-box/).
I use MVVM & WPF. The above user control works when you write the "Search"-event in the code-behind file of the View, but I can't get it to work with a command (using the ViewModel).
(The search-event fires when you haven't typed something for about 2 seconds.)
I've tried using Caliburn, so it can "map" the view event to the viewmodel method. However when the event fires, the application crashes: "No target found for method SearchText()." on the RaiseSearchEvent method from the custom user control.
See the following test application: Test application
Could somebody tell me what I'm doing wrong? I told CaliBurn to do the following:
<i:Interaction.Triggers>
<i:EventTrigger EventName="Search">
<cal:ActionMessage MethodName="SearchText()" />
</i:EventTrigger>
</i:Interaction.Triggers>
So I figure this is correct. It means that when the "Search" event fires, caliburn will look for the method SearchText in the ViewModel. This doesn't happen though, and it causes my app to crash and burn.
Do you know why? Or how I could solve this problem (doesn't have to be with Caliburn).
I already tried adding "Extending Command Support" (http://msdn.microsoft.com/en-us/library/dd458928.aspx), but this is a little too complex for me :/
Thanks for any help!!
You are using the Caliburn's ActionMessage but because you do not use its Bootstrapper class to start up your application, the MainView's DataContext is not set to an instance of the MainViewModel. If you check the SearchTextBox's DataContext at runtime, you'll see it's null.
Here's a series of steps that may solve your problem (using your linked example project)
Create a class called MyBootstrapper. It should look like this
public class MyBootstrapper : Bootstrapper<MainViewModel> {}
Add your new bootstrapper to the Application's Resources collection, like I show below (App.xaml)
<Application xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WpfApplicationParadise"
x:Class="WpfApplicationParadise.App">
<Application.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary>
<local:MyBootstrapper x:Key="bootstrapper" />
</ResourceDictionary>
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Application.Resources>
</Application>
Not sure why, but if the bootstrapper isn't nested in my build, it never is instantiated when App.InitializeComponent() is run...
Change App.xaml.cs to simply run InitializeComponent. Note that I had to tweak your build a bit to get this to work... InitializeComponent() is only defined in the App.g.cs file if you have the nested resource dictionary from step 2, or if you have an x:Name attribute on App.xaml, or perhaps other things...
using System.Windows;
namespace WpfApplicationParadise
{
public partial class App : Application
{
public App()
{
InitializeComponent();
}
}
}
Finally, you need to remove the parens as Wallstreet Programmer suggested.
Those steps should cause your App to instantiate your bootstrapper, which in turn instantates the MainViewModel as the root viewmodel of your application, and then create a MainView and hook up its DataContext to the MainViewModel. At that point, your application should work as expected.
Remove ()
<cal:ActionMessage MethodName="SearchText" />
After I run your application, I see that you need to initialize the MainViewModel and also to bind Text of SearchTextBox with TekstBoxTekst.
Codebehind
public partial class MainView : Window
{
public MainView()
{
InitializeComponent();
this.Loaded += (s, e) =>
{
this.DataContext = new MainViewModel();
};
}
}
XAML
<StackPanel>
<l:SearchTextBox
Text="{Binding TekstBoxTekst, UpdateSourceTrigger=PropertyChanged}"
Height="24" x:Name="TekstBoxTekst" Margin="145,144,145,143">
<i:Interaction.Triggers>
<i:EventTrigger EventName="Search">
<cal:ActionMessage MethodName="SearchText">
</cal:ActionMessage>
</i:EventTrigger>
</i:Interaction.Triggers>
</l:SearchTextBox>
</StackPanel>

C# User Control that can contain other Controls (when using it)

I found something about this issue for ASP, but it didn't help me much ...
What I'd like to do is the following: I want to create a user control that has a collection as property and buttons to navigate through this collection. I want to be able to bind this user control to a collection and display different controls on it (containing data from that collection).
Like what you had in MS Access on the lower edge of a form ...
to be more precise:
When I actually use the control in my application (after I created it), I want to be able to add multiple controls to it (textboxes, labels etc) between the <myControly> and </mycontrol>
If I do that now, the controls on my user control disappear.
Here is an example of one way to do what you want:
First, the code - UserControl1.xaml.cs
public partial class UserControl1 : UserControl
{
public static readonly DependencyProperty MyContentProperty =
DependencyProperty.Register("MyContent", typeof(object), typeof(UserControl1));
public UserControl1()
{
InitializeComponent();
}
public object MyContent
{
get { return GetValue(MyContentProperty); }
set { SetValue(MyContentProperty, value); }
}
}
And the user control's XAML - UserControl1.xaml
<UserControl x:Class="InCtrl.UserControl1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Height="300" Width="300" Name="MyCtrl">
<StackPanel>
<Button Content="Up"/>
<ContentPresenter Content="{Binding ElementName=MyCtrl, Path=MyContent}"/>
<Button Content="Down"/>
</StackPanel>
</UserControl>
And finally, the xaml to use our wonderful new control:
<Window x:Class="InCtrl.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:me="clr-namespace:InCtrl"
Title="Window1" Height="300" Width="300">
<Grid>
<me:UserControl1>
<me:UserControl1.MyContent>
<Button Content="Middle"/>
</me:UserControl1.MyContent>
</me:UserControl1>
</Grid>
</Window>
I'm having a hard time understanding your question, but I think what you're describing is an ItemsControl using DataTemplates to display the contents of (presumably) an ObservableCollection(T).
A UserControl may not be the best way to do this. You're wanting to add decorations around content, which is basically what Border does: it has a child element, and it adds its own stuff around the edges.
Look into the Decorator class, which Border descends from. If you make your own Border descendant, you should be easily able to do what you want. However, I believe this would require writing code, not XAML.
You might still want to make a UserControl to wrap the buttons at the bottom, just so you can use the visual designer for part of the process. But Decorator would be a good way to glue the pieces together and allow for user-definable content.
Here's a link to a built-in control (HeaderedContentControl) that does the same thing as the accepted answer except that it is an existing control in WPF since .Net 3.0

Categories

Resources