Wpf Usercontrol + Dependency Injection + DependencyProperty (+ViewModel) - wpf

Okay, i have to ask this question. I searched a lot but i have 2 problems, and i only find solutions so solfe one of it, but never both.
so what do i have:
i have a UserControl
<UserControl
x:Class="Project.UserControls.UserDetailsControl"
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:prism="http://www.codeplex.com/prism"
d:DesignHeight="450"
d:DesignWidth="800"
prism:ViewModelLocator.AutoWireViewModel="True"
mc:Ignorable="d">Some Controls in here</UserControl>
as you see, i am using prism. and i use also Dependency Injection with prism.Dryloc
the Code Behind:
using Project.Assistant.Common.Models;
using Project.GraphQL.Client;
using System.Windows;
using System.Windows.Controls;
namespace Project.Assistant.UserControls;
/// <summary>
/// Interaktionslogik für UserDetailsControl.xaml
/// </summary>
public partial class UserDetailsControl : UserControl
{
public User CurrentUser { get; set; }
public static readonly DependencyProperty SomeNumberProperty= DependencyProperty.Register(nameof(CurrentUser),typeof(User),typeof(UserDetailsControl));
public UserDetailsControl()
{
InitializeComponent();
}
}
These UserControl is then used like a normal Control. And in oder to get the Data into this Control, i Bind the CurrentUser like so:
<controls:UserDetailsControl Grid.Row="1" CurrentUser="{Binding CurrentUser}" />
What do i want to do:
i want to open a new Dialog out of this UserControl.
in order to open a new dialog, i want to use the IDialogService from Prism.
But when i add this to the Contructor, it simply crashes when i want to access the Usercontrol because it cannot Inject this dependency.
problem number too:
if i would simply just use a VieweModel and ViewModelLocator.AutoWireViewModel="True"
i dont know how i could implement my DependencyProperty.
here i read stuff like: "not everything needs a view model" (would solve the DependencyProperty)
and stuff like: "use viewmodels even for Usercontrols" (would solve the dependency injection, but cause problems with DependencyProperty)
what could i do?

Related

Microsoft WPF Designer Value For Property Not Updating When Changed By Another Property

Similar questions have been asked for WinForms projects, but not for WPF projects.
This VERY simple sample below shows the issue. The real user control I have is much more complicated but this sample shows the basic issue I am having.
The UserControl1 xaml (nothing there as it doesn't need anything to show the issue)
<UserControl x:Class="TestProperty.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="450" d:DesignWidth="800">
<Grid>
</Grid>
</UserControl>
The Window xaml (just contains a UserControl1):
<Window
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:TestProperty="clr-namespace:TestProperty;assembly=TestProperty" x:Class="TestApp.MainWindow"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<Grid>
<TestProperty:UserControl1 HorizontalAlignment="Left" Height="100" Margin="10,10,0,0" VerticalAlignment="Top" Width="100"/>
</Grid>
</Window>
The actual Window code-behind is really nothing
using System.Windows;
namespace TestApp
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
}
}
Here is code-behind for UserControl1. Has two properties where the first property simply modifies the second to be the same.
using System.ComponentModel;
using System.Windows;
using System.Windows.Controls;
namespace TestProperty
{
/// <summary>
/// Interaction logic for UserControl1.xaml
/// </summary>
public partial class UserControl1 : UserControl
{
public UserControl1()
{
InitializeComponent();
}
[Description("Sets a test property value that sets another property.")]
[Category("UserControl1")]
[RefreshProperties(RefreshProperties.All)]
public bool TestValue1
{
get => (bool) GetValue(TestValue1Property);
set => SetValue(TestValue1Property, value);
}
public static readonly DependencyProperty TestValue1Property =
DependencyProperty.Register(
"TestValue1", typeof(bool), typeof(UserControl1),
new FrameworkPropertyMetadata(false,
new PropertyChangedCallback(TestValue1Changed)));
private static void TestValue1Changed(DependencyObject d, DependencyPropertyChangedEventArgs e) =>
((UserControl1) d)?.TestValue1Changed(e);
private void TestValue1Changed(DependencyPropertyChangedEventArgs e)
{
TestValue2 = (bool) e.NewValue;
InvalidateProperty(TestValue2Property);
}
[Description("Value should change automatically when TestValue1 changes.")]
[Category("UserControl1")]
[RefreshProperties(RefreshProperties.Repaint)]
public bool TestValue2
{
get => (bool) GetValue(TestValue2Property);
set => SetValue(TestValue2Property, value);
}
public static readonly DependencyProperty TestValue2Property =
DependencyProperty.Register(
"TestValue2", typeof(bool), typeof(UserControl1),
new FrameworkPropertyMetadata(false,
new PropertyChangedCallback(TestValue2Changed)));
private static void TestValue2Changed(DependencyObject d, DependencyPropertyChangedEventArgs e) =>
((UserControl1) d)?.TestValue2Changed(e);
private void TestValue2Changed(DependencyPropertyChangedEventArgs e)
{
InvalidateProperty(TestValue2Property);
}
}
}
The attribute RefreshProperties and the call to InvalidateProperty appear to do nothing.
When editing the MainWindow.xaml and selecting UserControl1, the UserControl1 section of the Properties window shows TestValue1 and TestValue2. If TestValue1 is changed, TestValue2 does NOT change. However, if you change to another Visual Studio (or Blend) editor window and then back to the MainWindow.xaml, then TestValue2 will be the same as TestValue1.
I cannot seem to find any method to automatically have TestValue2 update when TestValue1 forces the update. The real case is obviously more complex with quite a few controls and when adding our UserControl, we want the designer to be able to specify several properties that can result in forcing (or coercing) other properties. However, the designer needs to be able to see the property value changes without having to switch to another file and back again to redraw the designer's properties window.
Is this something that simply can't be done - or is there something I'm missing. Note that this is a UserControl and is not designed to operate itself MVVM, but allow another window to be designed using the control with (or without) MVVM design.
Also, in a 'kind of' related issue. We have one property that needs to have an entry in the properties the same as Width and Height with the auto button. When set to Auto display "Auto (nnn)" as the automatically set value changes nnn changes. When not set, the value is simply nnn and is defined during the design of the window using the UserControl. Is this possible?
Thanks in advance to anyone who can shed light on this.

Navigation through button command in viewmodel

I have a WPF application with two pages, now I wanted to navigate to the other page when the button in first the page is clicked (I wrote the command for button in the first page), but the logic should be through the viewmodel. How to achieve this?
When I write WPF applications that need to navigate to different pages, I like to follow Rachel Lim's method to implement it using DataTemplates and ViewModels. You can follow the link to her page to get the exact code for the solution, but I'll give a little summary of her method here.
In her method, she creates a ViewModel that represents the application and has a property called CurrentPage which holds a ViewModel. You can then create a command on the ApplicationViewModel called ChangePage. This command will take the ViewModel that is passed as a parameter and sets it to the CurrentPage.
The xaml takes the responsibility of switching out the correct views. When using this method, I put a ContentControl in my MainWindow and bind the Content property to ApplicationViewModel.CurrentPage. Then in the resources of the MainWindow, I create DataTemplates to tell the view "When I try to display this ViewModel, put that View on the screen".
You don't really provide any code. But I assume your Navigation is in your code behind. You could do this by binding a Command OneWayToSource.
XAML
<local:MainWindow x:Class="WpfNameSpace.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:WpfNameSpace"
xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
mc:Ignorable="d"
NavigatePageCommand="{Binding Path=MyViewModel.NavigateCommand, Mode=OneWayToSource}"
Title="MainWindow" Height="600" Width="800">
<Grid>
</Grid>
</local:MainWindow>
Please take a look at local:MainWindow.
C#
public partial class MainWindow : Window
{
public ICommand NavigatePageCommand
{
get { return (ICommand) GetValue(NavigatePageCommandProperty); }
set { SetValue(NavigatePageCommandProperty, value); }
}
// Using a DependencyProperty as the backing store for NavigatePageCommand. This enables animation, styling, binding, etc...
public static readonly DependencyProperty NavigatePageCommandProperty =
DependencyProperty.Register("NavigatePageCommand", typeof(ICommand), typeof(MainWindow),
new PropertyMetadata(0));
public MainWindow()
{
InitializeComponent();
NavigatePageCommand = new RelayCommand(Navigate);
}
public void Navigate()
{
//Do Navigation here
}
}
I assume you are familiar with Commands, ViewModels and Bindings and you get the idea.

Calling a custom dependency property defined in code-behind from XAML

Is it possible to call a custom dependency property in the XAML of the element in which it is defined?
I mean, i have the following simple code for my mainWindow:
Code
public partial class MainWindow : Window
{
public static readonly DependencyProperty SpecialToProperty = DependencyProperty.Register("SpecialTo", typeof(double), typeof(MainWindow));
public MainWindow()
{
InitializeComponent();
}
public double SpecialTo
{
get
{
return (double)GetValue(SpecialToProperty);
}
set
{
SetValue(DoubleAnimation.ToProperty, value);
}
}
}
How can i use that dependency property from the XAML partial code of the MainWindow class?
I mean something like:
<Window x:Class="WpfAnimationTEst.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"
SpecialTo=200>
I know it can be done using attached dependency properties, but is it the only way? Is it not possible to call a dependency property defined in the code-behind?
Thank you and sorry if the question is some kind of stupid, i'm just learning and trying to understand WPF.
I found the answer after I initially posted a wrong answer:
The problem really lies in circular dependencies if you use andreask's answer. I had to create a BaseClass for all windows:
1) Create a new Window Base Class:
public class BaseWindow : Window {
public BaseWindow() { }
public static readonly DependencyProperty SpecialToProperty = DependencyProperty.Register("SpecialTo", typeof(double), typeof(BaseWindow));
public double SpecialTo {
get {
return (double)GetValue(SpecialToProperty);
}
set {
SetValue(SpecialToProperty, value);
}
}
}
This will be the new baseclass for all your windows.
2) Modify your MainWindow xaml: (Change YOURNAMESPACE (2x) to your namespace name)
<local:BaseWindow x:Class="YOURNAMESPACE.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:YOURNAMESPACE"
Title="MainWindow" Height="350" Width="525" SpecialTo="100">
<Grid>
</Grid>
</local:BaseWindow>
3) And you also need to modify your partial MainWindow.cs:
public partial class MainWindow : BaseWindow {
public MainWindow() {
InitializeComponent();
}
}
That worked for me, however, you will always need to use the extra xaml markup in your window declaration.
I'm answering my own question because there seems to be many ways to solve it correctly. I've upvoted the answers that best helped me, but i can't set any as the correct answer since all are correct.
So i'll just post a conclusion. If you think that i'm mistaken, please post a comment and i will correct my mind.
The main answer to my question is no, it is not possible to directly call a custom dependency property defined at code-behind from its "linked" XAML file. It is mandatory to instantiate the control in which the property is defined to call it.
To me, the best workarrounds to use a custom dependency property in XAML, defined in the code-behind are the posted by #Clemens and #Noel Widmer. This and this
You can use custom dependency properties in XAML, but only if you instantiate the control in XAML. For example, take a customized TextBox element:
public class MyTextBox : TextBox
{
public static readonly DependencyProperty SpecialToProperty = DependencyProperty.Register("SpecialTo", typeof(double), typeof(MyTextBox));
public double SpecialTo
{
get
{
return (double)GetValue(SpecialToProperty);
}
set
{
SetValue(DoubleAnimation.ToProperty, value);
}
}
}
You can of course create an instance of MyTextBox in XAML and assign the SpecialTo property there:
<custom:MyTextBox SpecialTo="1.0" />
In your case, however, you're not instantiating the custom class MainWindow, but you create a new instance of class Window, and the Window class isn't aware of the custom dependency property (the SpecialTo property is not even available in Window, since you declared it within the MainWindow class).
For the dependency property to be recognized, you'd need to instantiate MainWindow directly:
<custom: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"
SpecialTo=200>
However, this means you need to omit the x:class directive that used to combine XAML and codebehind of your window (otherwise you'd run into circular dependencies), and I'm not sure if this correctly initalizes your window...
Yes, it is possible. Dependency properties are used to bind within XAML. If you want to bind to property defined in the code behind window you need to reference this window as XAML element, i.e. add tag for your main window x:Name="mainWindow", and next in the binding expression refer it as ElementName=mainWindow

WindowsFormsHost steals focus on activating application even activating by application's other wpf form

To Reproduce my case (.net 4.0)
Create a WPF Application (MainWindow.xaml)
Add a Winform user control that contains a textbox (UserConrol1.cs - Winform)
Put UserControl1 into MainWindow.xaml with windowsformshost
Add another WPF Window that contains a textbox(wpf) to project (Window1.xaml)
Create and Show Window1 after MainWindow InitializeComponent
Your project is ready,
Run Project and set textbox focused in MainWindow.xaml (that in WindowsFormsHost)
Deactivate your application by opening a window (Windows file explorer ,notepad, winamp etc.)
Try to write in textbox that in Window1 window by clicking textbox with mouse
And you will see that you can't set focus on textbox in Window1 because MainWindow Texbox( in winformshost will steal your focus on you application got activating)
Any idea?
MainWindow.xaml
<Window x:Class="WinFormsHostFocusProblem.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WinFormsHostFocusProblem"
xmlns:my="clr-namespace:System.Windows.Forms.Integration;assembly=WindowsFormsIntegration"
Title="MainWindow" Height="350" Width="525">
<Grid>
<my:WindowsFormsHost Focusable="False" >
<local:UserControl1>
</local:UserControl1>
</my:WindowsFormsHost>
</Grid>
</Window>
MainWindow.xaml.cs
namespace WinFormsHostFocusProblem
{
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
Window1 window1 = new Window1();
window1.Show();
}
}
}
Window1.xaml
<Window x:Class="WinFormsHostFocusProblem.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WinFormsHostFocusProblem"
xmlns:my="clr-namespace:System.Windows.Forms.Integration;assembly=WindowsFormsIntegration"
SizeToContent="WidthAndHeight"
ResizeMode="NoResize"
Topmost="True"
Title="Window1" Height="300" Width="300" Background="Red">
<Grid>
<TextBox Height="25">asd</TextBox>
</Grid>
</Window>
Window1.xaml.cs
namespace WinFormsHostFocusProblem
{
public partial class Window1 : Window
{
public Window1()
{
InitializeComponent();
}
}
}
I used my MSDN support contract to get an answer to this problem. The engineer was able to repro from yunusayd's sample and confirmed it is almost certainly a bug in WindowsFormsHost.
Thanks to yunus for the minimal repro sample and Keith at Microsoft for tackling the issue and providing a workaround in less than one day.
Workaround code follows. It works by using .NET reflection to change a private variable used in WindowsFormsHost and disable the trigger for the bug. According to the engineer I worked with, this relies on WPF internals, but he spoke with product team members and it should be safe to use. There's no guarantee of lack of side effects, of course, but so far I haven't found any problems in my testing with multiple WindowsFormsHosts in multiple WPF windows (maybe nesting would be trickier). I modified the original workaround to work generically with multiple windows. You can just as easily hardcode references to specific windows and named WindowsFormsHost controls in the Application_Deactivated event and skip the whole "LastActive" scheme and extension methods.
// App.xaml.cs: you must hook up to Application.Deactivated
void Application_Deactivated(object sender, EventArgs e)
{
foreach (Window w in windows)
{
foreach (var host in UI.DependencyObjectExtension.AllLogicalChildren(w).
Where(c => c is WindowsFormsHost))
{
FIELD_FOCUSED_CHILD.SetValue(host, null);
}
}
}
public readonly static FieldInfo FIELD_FOCUSED_CHILD = typeof(System.Windows.Forms.Integration.WindowsFormsHost).
GetField("_focusedChild", BindingFlags.NonPublic | BindingFlags.Instance);
public static class DependencyObjectExtension
{
/// <summary>
/// Returns a collection of o's logical children, recursively.
/// </summary>
/// <param name="o"></param>
/// <returns></returns>
public static IEnumerable<DependencyObject> AllLogicalChildren(this DependencyObject o)
{
foreach (var child in LogicalTreeHelper.GetChildren(o))
{
if (child is DependencyObject)
{
yield return (DependencyObject)child;
if (child is DependencyObject)
{
foreach (var innerChild in AllLogicalChildren((DependencyObject)child))
{
yield return innerChild;
}
}
}
}
}
}
We had a similar problem in one of our applications and found that upgrading to .net 4.5 seems to have fixed a good portion of our application's WPF/WinForms focus issues including a similar one to this.
In addition, the _focusedChild field no longer exists in the .net 4.5 version of WindowsFormsHost

Expose UserControl property to XAML

WPF controls have certain properties (UserControl.Resources, UserControl.CommandBindings) that can have items added to them from the XAML of a user control declaration. Example:
<UserControl ... >
<UserControl.CommandBindings>
...
</UserControl.CommandBindings>
<UserControl.Resources>
...
</UserControl.Resources>
</UserControl>
I have a new list property defined in my user control:
public partial class ArchetypeControl : UserControl {
...
public List<Object> UICommands { get; set; }
I want to add items to this list like I can with resources and CommandBindings, but when I do this:
<c:ArchetypeControl.UICommands>
</c:ArchetypeControl.UICommands>
I get the error "Error 4 The attachable property 'UICommands' was not found in type 'ArchetypeControl'. "
Suggestions?
-
Given the comments, I've created a test control to show the entire code and reproduce the problem. I'm using visual studio 2010.
<UserControl x:Class="ArchetypesUI.TestControl"
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:c="clr-namespace:ArchetypesUI"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300">
<c:TestControl.TestObject>
</c:TestControl.TestObject>
<Grid>
</Grid>
</UserControl>
-
namespace ArchetypesUI
{
/// <summary>
/// Interaction logic for TestControl.xaml
/// </summary>
public partial class TestControl : UserControl
{
public Object TestObject { get; set; }
public TestControl()
{
InitializeComponent();
}
}
}
Now the error I get is "Error 2 The attached property 'TestControl.TestObject' is not defined on 'UserControl' or one of its base classes."
Take a look at your XAML:
<UserControl>
^^^^^^^^^^^
<c:TestControl.TestObject>
^^^^^^^^^^^
</c:TestControl.TestObject>
</UserControl>
Here, you are declaring a UserControl, and then trying to set a TestControl property on it. Since UserControl doesn't have the TestControl.TestObject property, WPF that it can't set that property on the UserControl object. You may say, "But I'm declaring a UserControl of type TestControl. My UserControl is a TestControl!" But that's not quite the case. The above declaration is declaring the TestControl class: it's not creating an instance of TestControl, so it can't have instance properties set on it.
Rather, the TestObject property is there for users of TestControl to set on individual instances of TestControl:
<local:TestControl>
<local:TestControl.TestObject> <!-- Now it will work -->
</local:TestControl.TestObject>
</local:TestControl>
If you want to set a default / initial value for the TestObject property, then you can do so either in the TestControl constructor, or (if TestObject is a dependency property) through the TestControl default style (though this is more for custom controls than for user controls).
I'm not quite able to recreate your issue... the case I've created seems to work. I did have to initialize the list in the constructor.
However, from your example I wonder a more appropriate place for your list source would be on a ViewModel object of some sort. If you're exposing commands, having an IEnumerable of some sort of a ICommand wrapper which also encapsulates the display elements you need (e.g. Caption, Icon URI, etc).
ViewModels are certainly not a panacea, but in this case I think it would let you put all the knowledge of the commands you want to use in the same place (e.g. which are available and what they do).

Resources