Inheriting from custom window in WPF - wpf

I have a custom window in WPF which I want to use as a base window for other windows.
When I tried to inherit it, I wrote in the XAML:
<my:MyWindow x:Class="NewWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:my="clr-namespace:MyNamesapce;assembly=MyAssembly"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300">
In the .cs code I wrote:
namespace SomeOtherNamespace
{
public partial class NewWindow: MyWindow
{
internal NewWindow(Control ctrl) : base(ctrl)
{
InitializeComponent();
this.ResizeMode = System.Windows.ResizeMode.NoResize;
}
}
}
But then I got the error:
cannot be the root of a XAML file because it was defined using XAML.
What am I doing wrong and how can I fix it?

If what you are trying to achieve is setting ResizeMode to NoResize in every window you could use a style like this:
<Style TargetType="Window" x:Key="windowStyle">
<Setter Property="ResizeMode" Value="NoResize" />
</Style>
Put this style in a ResourceDictionary and make it be the window style:
Style="{StaticResource windowStyle}"
But if you want to go further you'll have to make a new class inheriting from Window
public class MyWindow : Window
{
public MyWindow()
{
this.ResizeMode = ResizeMode.NoResize;
}
}
Now you are able to instanciate a new MyWindow
<mn:MyWindow x:Class="Project.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mn="clr-namespace:MyControls"
Height="300" Width="300">
</mn:MyWindow>
Be aware the class that will be the "code behind" of this new window need to inherit from your new MyWindow class as below:
namespace Project
{
public partial class Window1 : MyControls.MyWindow
{
public Window1()
{
InitializeComponent();
}
}
}

Related

Binding UserControl Dependency Property and MVVM

I have a MainWindow containing a UserControl, both implemented in MVVM-pattern.
The MainWindowVM has properties that I want to bind to properties in the UserControl1VM. But this doesn't work.
Here's some code (the viewmodels use some kind of mvvm-framework that implement the INotifyPropertyChanged in a ViewModelBase-class but that's hopefully no problem):
MainWindow.xaml:
<Window x:Class="DPandMVVM.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:DPandMVVM"
Title="MainWindow" Height="300" Width="300">
<Grid>
<local:UserControl1 TextInControl="{Binding Text}" />
</Grid>
</Window>
CodeBehind MainWindow.xaml.cs:
using System.Windows;
namespace DPandMVVM
{
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
DataContext = new MainWindowVM();
}
}
}
MainWindow-ViewModel MainWindowVM.cs:
namespace DPandMVVM
{
public class MainWindowVM : ViewModelBase
{
private string _text;
public string Text { get { return _text; } }
public MainWindowVM()
{
_text = "Text from MainWindowVM";
}
}
}
And here the UserControl1.xaml:
<UserControl x:Class="DPandMVVM.UserControl1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300">
<Grid>
<TextBlock Text="{Binding TextInTextBlock}" />
</Grid>
</UserControl>
The Codebehind UserControl1.xaml.cs:
using System.Windows.Controls;
namespace DPandMVVM
{
/// <summary>
/// Interaction logic for UserControl1.xaml
/// </summary>
public partial class UserControl1 : UserControl
{
public UserControl1()
{
InitializeComponent();
DataContext = new UserControl1VM();
}
}
}
And the Viewmodel UserControl1VM.cs:
using System.Windows;
namespace DPandMVVM
{
public class UserControl1VM : DependencyObject
{
public UserControl1VM()
{
TextInControl = "TextfromUserControl1VM";
}
public string TextInControl
{
get { return (string)GetValue(TextInControlProperty); }
set { SetValue(TextInControlProperty, value); }
}
public static readonly DependencyProperty TextInControlProperty =
DependencyProperty.Register("TextInControl", typeof(string), typeof(UserControl1VM));
}
}
With this constellation the DP cannot be found in MainWindow.xaml.
What am I doing wrong?
First of all you want DependencyProperty TextInControl to be declared inside UserControl1 if you want to bind it from outside.
Move the declaration of DP inside of UserControl1.
public partial class UserControl1 : UserControl
{
public UserControl1()
{
InitializeComponent();
}
public string TextInControl
{
get { return (string)GetValue(TextInControlProperty); }
set { SetValue(TextInControlProperty, value); }
}
public static readonly DependencyProperty TextInControlProperty =
DependencyProperty.Register("TextInControl", typeof(string),
typeof(UserControl1));
}
Second you have externally set DataContext of UserControl to UserControl1VM,
public UserControl1()
{
InitializeComponent();
DataContext = new UserControl1VM(); <-- HERE (Remove this)
}
So WPF binding engine looking for property Text in UserControl1VM instead of MainWindowVM. Remove setting DataContext and update XAML of UserControl1 to this:
<UserControl x:Class="DPandMVVM.UserControl1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300"
x:Name="userControl1">
<Grid>
<TextBlock Text="{Binding TextInTextBlock, ElementName=userControl1}" />
</Grid>
</UserControl>
Bind DP using ElementName by setting x:Name on UserControl.
UPDATE
In case you want to have ViewModel intact for UserControl, you have to update binding in MainWindow. Explicitly tell WPF binding engine to look for property in MainWindow's DataContext using ElementName in binding like this:
<local:UserControl1 TextInControl="{Binding DataContext.Text,
ElementName=mainWindow}" />
For this you need to set x:Name="mainWindow" on window root level.
The XAML of your control right now reference the property TextInTextBlock via the DataContext which in turn "Points" to your main window's view model. Reference the data of the control and you are done (btw do not set the DataContext for that reason - the binding won't work any more):
<UserControl x:Class="DPandMVVM.UserControl1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300"
x:Name="self">
<Grid>
<TextBlock Text="{Binding TextInTextBlock, ElementName=self}" />
</Grid>
</UserControl>
This is how I do UserControls with MVVM and DP binding. It's similar to Rohit's answer but with some slight changes. Basically you need to set the Control's internal view model to be the DataContext of the root container within the UserControl rather than the UserControl itself, that way it will not interfere with DP bindings.
E.g.
UserControl XAML
<UserControl x:Class="DPandMVVM.UserControl1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300"
x:Name="userControl1">
<Grid x:Name="Root">
<TextBlock Text="{Binding TextFromVM}" />
</Grid>
UserControl Code-behind
public partial class UserControl1 : UserControl
{
public UserControl1()
{
InitializeComponent();
this.ViewModel = new UserControlVM();
}
public UserControlVM ViewModel
{
get { return this.Root.DataContext as UserControlVM ; }
set { this.Root.DataContext = value; }
}
public string TextFromBinding
{
get { return (string)GetValue(TextFromBindingProperty); }
set { SetValue(TextFromBindingProperty, value); }
}
public static readonly DependencyProperty TextFromBindingProperty =
DependencyProperty.Register("TextFromBinding", typeof(string), typeof(UserControl1), new FrameworkPropertyMetadata(null, OnTextBindingChanged));
private static void OnTextBindingChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var uc = d as UserControl1;
uc.ViewModel.TextFromVM = e.NewValue as string;
}
}
This means that the control derives it's values from the Root element DataContext which is our ViewModel but the ViewModel can be updated via a DP binding from outside the control (in your case a binding to the parent Window's ViewModel, see below)
Window XAML
<Window x:Class="DPandMVVM.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:DPandMVVM"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300"
x:Name="window1">
<Grid x:Name="Root">
<local:userControl1 TextFromBinding="{Binding TextFromWindowVM}" />
</Grid>
I have a method that I believe is a lot simpler, and probably more true to MVVM.
In the main window XAML:
<myNameSpace:myUserControl DataContext="{Binding Status}"/>
In your main view model (the data context of the main window:
public myUserControlViewModel Status { set; get; }
now you can in the constructor (or whenever you want to instantiate it):
Status = new myUserControlViewModel();
then if you want to set the text property:
Status.Text = "foo";
and make sure you have the binding setup to a property named Text inside your myUserControlViewModel class:
<TextBox Text="{Binding Text}"/>
and make sure the property fires PropertyChanged, of-course.
Plus, if you use Resharper. You can create a Design instance of the UserControl in your XAML so that it can link the bindings and not tell you that the property is never used by doing this:
<UserControl x:Class="myNameSpace.myUserControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:myNameSpace="clr-namespace:myNameSpace"
d:DataContext="{d:DesignInstance myNameSpace.myUserControl}"
mc:Ignorable="d" ...>
This part:
xmlns:myNameSpace="clr-namespace:myNameSpace"
d:DataContext="{d:DesignInstance myNameSpace.myUserControl}"
Here's a possible working solution for you. However, I've noted in a comment above that this will work in code and perhaps (like my situation) will show up as an error (Object Not Found) in the designer:
<local:UserControl1 TextInControl="{Binding DataContext.Text,
Source={x:Reference <<Your control that contains the DataContext here>>}}" />
I'd rather to have a cleaner solution, though, without any designer errors. I wish to find out how to properly bind a dependency property in a user control to a value coming from the window it's contained in. What I'm finding is that whatever I try to do (short of what I showed above), such as using ElementName and/or AncestorType/Level, etc., the debugger complains that it can't find the source and shows that it's looking for the source inside the context of the user control! It's like I can't break out of the user control context when doing Binding logic in the use of that control (other than that "designer-breaking" solution above).
UPDATE:
I noticed that this might not work for you as your situation might force a problem I just noticed if I change my own source to reference the window instead of a control that has the data context. If I reference the window then I end up with a cyclical redundancy. Perhaps you'll figure out a way to use the Source version of the binding that will work okay for you.
I must also add that my situation is probably a bit more complex since my usercontrol is used in the context of a popup.

Handle same events from different windows

I have a WPF project with two windows, the first window firing events and have it's own event handlers, the other window will fire the same events, any idea how to use handlers in first window to handle events in second window ?
thanks to #Cody Gray , #keyboardP and to this useful article http://geekswithblogs.net/lbugnion/archive/2007/03/02/107747.aspx
Here's a code snippet to demonstrate the answer:
first step add a new subclass :
using System.Windows;
namespace WpfApplication
{
public class Subclass : Window
{
//Event handlers,functions or any potential reused code
}
}
Second step: go to window1.xaml.cs
namespace WpfApplication
{
public partial class Window1 : Subclass
{
public Window1()
{
InitializeComponent();
}
}
}
Third step: change the Xaml code for window1 as below:
<src:Subclass
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:src="clr-namespace:WpfApplication"
Title="Window1" Height="350" Width="525">
</src:Subclass>
Finally do 2nd & 3rd steps for window2
A simple sample of what i was proposing in first place:
<Window x:Class="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>
<Window Loaded="Window_Loaded"/>
<Window Loaded="Window_Loaded"/>
</Grid>
</Window>
Look how both windows share the event handler:
public partial class Window1 : Window
{
public Window1()
{
InitializeComponent();
}
private void Window_Loaded(object sender, RoutedEventArgs e)
{
//Whatever
}
}
At this point you should ask yourself if you really needed two windows, or any other container control could be enough.

Making UserControls accessible in WPF

I have a WPF form, which consists of a grid of two columns.
In the left-hand column are the control labels, and in the right-hand column are my controls.
The controls are all UserControls. In the simplest case, some of these controls simply wrap existing WPF controls such as the textbox, so that they all implement a common interface.
When the form is generated, I have code like this to set the label for the associated control, where newControl is the created UserControl and ctl.Caption simply returns the required label text:
Label newLabel = new Label();
newLabel.Content = ctl.Caption + ":";
newLabel.Target = newControl;
One problem is that setting the Target doesn't actually work. If I have an underscore in the caption, the mnemonic key doesn't set focus to the wrapped control. One workaround for this may be to manually set the focus to the wrapped control within the UserControl code - but...
The biggest problem is accessibility. Screenreaders such as JAWS, and Windows built-in Narrator, do not read the control caption when the control receives focus.
I have had a look at this: http://msdn.microsoft.com/en-us/library/windows/desktop/gg712258.aspx - which provides a lot of detail, but no helpful examples. It has a lot of stuff about custom controls, which is surely overkill for a simple user control?
So, how can I "attach" my labels correctly to my UserControls?
You can browse the code for the entire project at http://quest.codeplex.com/SourceControl/changeset/view/676933506953 - the particular code is in the EditorControls project, and the UserControls are instantiated in ElementEditor.xaml.cs.
Your newControl is of type Control that doesn't allow you to add additional content.
If you want to add some content to it you need to use a class that supports it, like ContentControl or Panel (for multiple childs) you can implement you own control that implements the IAddChild interface.
A simple solution for you problem could be :
<UserControl x:Class="UIMocks.MyCustomControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<StackPanel x:Name="content"/>
</UserControl>
The code-behind
[ContentProperty("Children")]
public partial class MyCustomControl : UserControl
{
public MyCustomControl()
{
InitializeComponent();
}
public UIElementCollection Children { get { return content.Children; } }
}
and then you can use
MyCustomControl newControl = InitialiseEditorControl(ctl);
...
Label newLabel = new Label();
newLabel.Content = ctl.Caption + ":";
newControl.Children.Add(newLabel);
hmmm I tried to reproduce your issue on a small test project, but for me it works... so I guess you'll have to give more details on how your userControls are built. Here is what works for me:
I created an Empty project (just the App and Window files, as usual) and set up a grid with 2 columns in my window:
<Window x:Class="Test.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow"
Name="Window"
SizeToContent="WidthAndHeight">
<Grid Name="MyGrid">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="100" />
<ColumnDefinition Width="100"/>
</Grid.ColumnDefinitions>
</Grid>
</Window>
then created a userControl that extends the wpf TextBox class:
<TextBox x:Class="Test.MyTextBox"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300">
</TextBox>
and:
using System.Windows;
using System.Windows.Controls;
namespace Test
{
public partial class MyTextBox : TextBox
{
public static readonly DependencyProperty CaptionProperty =
DependencyProperty.Register("Caption", typeof(string), typeof(MyTextBox), new UIPropertyMetadata(""));
public string Caption
{
get { return (string)GetValue(CaptionProperty); }
set { SetValue(CaptionProperty, value); }
}
public MyTextBox()
{
InitializeComponent();
}
}
}
it's basically a textbox with a "Caption" dp.
and now in My window's code behind:
public MainWindow()
{
InitializeComponent();
MyTextBox tb = new MyTextBox { Caption = "_Foo", Width = 100 };
Label lb = new Label { Content = tb.Caption + ":", Target = tb };
MyGrid.Children.Add(lb);
MyGrid.Children.Add(tb);
Grid.SetColumn(lb, 0);
Grid.SetColumn(tb, 1);
}
and with this, I do get focus on the TB when I press ALT + F (I can even see the _ under the F of "Foo" in the Label when just pressing ALT)
So I guess your issue has to do with your UserControls themselves and how they are built (what Template for instance)
Edit:
If your control is not extending an existing control but rather contains a WPF control, the issue is probably on the Focus method. You should add a Focus() method that sets the focus on the right part of your control when the control itself gets the focus.
code (for a UserControl containing a textbox that you want to get the focus for instance):
<TextBox x:Class="Test.MyTextBox"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<Button Content="foo" Grid.Column="0" />
<TextBox Name="TextBoxPart" Grid.Column="1" />
</Grid>
</TextBox>
code behind
public partial class MyTextBox : TextBox
{
public static readonly DependencyProperty CaptionProperty =
DependencyProperty.Register("Caption", typeof(string), typeof(MyTextBox), new UIPropertyMetadata(""));
public string Caption
{
get { return (string)GetValue(CaptionProperty); }
set { SetValue(CaptionProperty, value); }
}
public MyTextBox()
{
InitializeComponent();
}
protected override void OnGotFocus(RoutedEventArgs e)
{
TextBoxPart.Focus();
}
}
Edit 2:
I had an issue once, to transfer the focus to a subcontrol in a dataGridCell, and here is what I did in the template:
<ControlTemplate.Triggers>
<Trigger Property="IsFocused" Value="True">
<Setter TargetName="TextBoxPart" Property="FocusManager.FocusedElement" Value="{Binding ElementName=TextBoxPart}" />
</Trigger>
</ControlTemplate.Triggers>
you could try adding this to your template. This should transfer your focus alright.
as for the accessibility, I don't think this will help, but I do not see any way of achieving what you want :-/

Invoke Command Within Custom User Control

I'm new to WPF and i'm trying to learn the famous MVVM pattern,
I'm facing a small issue (i'm sure) when i try to bind simple command to some ViewModel
this is Simple UserControl i've created:
<UserControl x:Class="MVVM_UsingUserControl_Sample.View.MyUserControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300"
>
<StackPanel DataContext="MyUserControlViewModel" Orientation="Vertical" >
<Button Margin="0,100,0,0" Height="50" Width="50" Content="PressMe" Command="{Binding Path=MyCommand}"/>
</StackPanel>
</UserControl>
and this is the User Control ViewModel
class MyUserControlViewModel : ViewModelBase
{
CommandBase m_MyCommand = null;
public MyUserControlViewModel()
{
}
public ICommand MyCommand
{
get
{
if (m_MyCommand == null)
{
m_MyCommand = new CommandBase(new Action<object>(DoSomething),
new Predicate<object>(CanDoSomething));
}
return m_MyCommand;
}
}
public void DoSomething(object i_Params)
{
MessageBox.Show("Inside MyUserControl DoSomething");
}
public bool CanDoSomething(object i_Params)
{
return true;
}
}
this is the Main window xaml (no code behaind)
Now the problem is :
My main window contains the userControl as is (inside stack panel) and nothing else.
i expect the command "MyCommad" will get invoke when i press the button "MyButton"
but it doesn't.
anyone has idea why ???
Big thanks.
In the constructor of your main window, set its DataContext to your ViewModel.
For example,
this.DataContext = new MyViewModel();
In your XAML, remove
DataContext="MyUserControlViewModel"
since the DataContext will inherit from the main window.
Everything should then work as you expect.

Why do I get a XamlParseException when I inherit a Custom UserControl in another project?

In one project I have an Editor Class:
namespace TestXamlInherit234
{
public class CustomerEditor : BaseEditor
{
public CustomerEditor()
{
TheMessage.Text = "changed222";
}
}
}
which inherits from a WPF User Control in another project:
using System.Windows.Controls;
namespace Core
{
public partial class BaseEditor : UserControl
{
public TextBlock TheMessage
{
get
{
return TheMessage2;
}
}
public BaseEditor()
{
InitializeComponent();
}
}
}
<UserControl x:Class="Core.BaseEditor"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Height="300" Width="300">
<Grid>
<TextBlock x:Name="TheMessage2" Text="This is in the base editor"/>
</Grid>
</UserControl>
This works when both classes are in the same project but when they are in two different projects, I get a XamlParseException error.
Try:
<Core:BaseEditor x:Class="TestXamlInherit234.CustomerEditor"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:Core="yourcorenamespace"
Height="300" Width="300">
<Grid>
<TextBlock x:Name="TheMessage2" Text="This is in the base editor"/>
</Grid>
</Core:BaseEditor>
WPF's support for inheriting any kind of UserControls is very limited. When I did this to work around the lack of generics support I had to define my control in code and derive from ContentControl.

Resources