Events and Properties accessible from outside of UserControl MVVM - wpf

So, I have a UserControl named ApplicationForm. It has some TextBoxes and a Button. Each TextBox has a Binding. An example of it:
<xctk:WatermarkTextBox Watermark="PACAPIME" Text="{Binding NumPac}" Grid.Column="2" Grid.Row="2" />
Then, my View is bound to a ViewModel like this:
<UserControl x:Class="Verkooporder"
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:xctk="http://schemas.xceed.com/wpf/xaml/toolkit"
xmlns:vm="clr-namespace:Koala" mc:Ignorable="d" d:DesignHeight="720" d:DesignWidth="1100">
<UserControl.Resources>
<vm:VerkooporderViewModel x:Key="VerkooporderViewModelDataSource"
d:IsDataSource="True"/>
</UserControl.Resources>
<UserControl.DataContext>
<Binding Source="{StaticResource VerkooporderViewModelDataSource}"/>
</UserControl.DataContext>
The corresponding property in my ViewModel is like this:
Private _oNumPac As String = ""
Public Property NumPac As String
Get
Return _oNumPac
End Get
Set(ByVal value As String)
_oNumPac = value
RaisePropertyChanged()
End Set
End Property
This is working great inside my UserControl. Now, I call my UserControl in my MainWindow:
<local:Verkooporder />
I want to be able to access the Property NumPac like <local:Verkooporder NumPac="" /> but this isn't working.
Also, how can I have access to the Button Command?
Public Shared _oNumPac As DependencyProperty = DependencyProperty.Register("NumPac",
GetType(String), GetType(VerkooporderViewModel), New PropertyMetadata(""))
Public Property NumPac As String
Get
Return GetValue(_oNumPac)
End Get
Set(value As String)
SetValue(_oNumPac, value)
End Set
End Property
But it doesn't show up when I try to type this:
<local:Verkooporder NumPac="Test" />
Notice that the properties are in the ViewModel of the UserControl, not the code behind View

Related

UserControl's DataContext: set it to viewmodel directly

I have a wpf usercontrol and a correspondent viewmodel (ChamberVm) made for it.
In viewmodel there is a property named 'UnitStatus'
But I got binding error:
System.Windows.Data Error: 40 : BindingExpression path error: 'UnitStatus' property not found on 'object' ''String' (HashCode=-1814504727)'. BindingExpression:Path=UnitStatus; DataItem='String' (HashCode=-1814504727); target element is 'VacGram' (Name='sysdgm'); target property is 'UnitStatus' (type 'KeyValuePair`2')
I have noted the mistake might be about DataContext setting in the header part of my control:
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:dgm="clr-namespace:VacSym;assembly=VacSymDgm"
xmlns:v="clr-namespace:VacViews"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="400"
DataContext="ChamberVm">
<Grid Name="gridMain">
<Grid.RowDefinitions>
<RowDefinition Height="0*"></RowDefinition>
<RowDefinition Height="*"></RowDefinition>
<RowDefinition Height="15"></RowDefinition>
</Grid.RowDefinitions>
<DockPanel Grid.Row="1">
<DockPanel x:Name="pnlDgm" Background="Transparent" HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
<dgm:VacGram x:Name="sysdgm" UnitStatus="{Binding UnitStatus}" DiagramFile="{Binding DiagramFile}" HorizontalAlignment="Stretch" VerticalAlignment="Stretch"/>
</DockPanel>
. . .
I want to know:
1, why this is not correct (?)
DataContext="ChamberVm"
2, what the 'String' means in the error message:
...not found on 'object' ''String'
Ting
The assignment
DataContext="ChamberVm"
assigns the string "ChamberVm" to the DataContext property.
In order to assign an instance of the ChamberVm class, you would have to write the following, with an appropriate namespace prefix:
<UserControl ...>
<UserControl.DataContext>
<v:ChamberVm/>
</UserControl.DataContext>
...
</UserControl>
In general, you should avoid to explicitly assign the DataContext property at all.
You would instead put the UserControl in a DataTemplate that is applied to e.g. a ContentControl that has a ChamberVm object assigned to its Content property. The DataContext of the UserControl would then be set automatically.
Your DataContext is a string "ChamberVm", not an object.
That's why you can't find UnitStatus.
<!-- Just set string in DataContext -->
<Window DataContext="ChamberVm">
</Window>
ChamberVm is string right.
So, of course, there is no UnitStatus (object) in DataContext (string).
And DataContext property is object type.
// DependencyProperty
public object DataContext { get; set; }
// You can put anything in.
It's a mistake that everyone makes often.
Anyway, you use DataContext in two main ways.
(To your liking)
1. DataContext Direct Binding in XAML.
<Window xmlns:vm="clr-namespace:VacViews.ViewModels">
<Window.DataContext>
<vm:ChamberVm/>
</Window.DataContext>
</Window>
You must declare the namespace of your view model at the top first. xmlns:vm="your namespace"
And open DataContext to declare the view model you created. This is to create an instance and assign it to DataContext.
2. Set Create Instance in Behind.
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
// Your ViewModel set in DataContext.
DataContext = new ChamberVm();
}
}
Creating a 'view model' instance within the constructor block is the most impressive position.
Feel free to let me know if you have any further questions.

How do I bind the value of a dependency property in the UserControl it's defined in XAML?

Suppose I have a UserControl which has a DataContext set to it's VM
Now suppose the UserControl has a dependency property defined that I want to initially bind to a property on the ViewModel ...how would I reference this property in the XAML of the UserControl given the top-level element is UserControl?
<UserControl x:Class="TestApp1.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:local="clr-namespace:TestApp1"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300" x:Name="test">
</UserControl>
Dependency property:
public static readonly DependencyProperty MyTestPropProperty= null;
public string MyTestProp
{
get { return (string)GetValue(MyTestPropProperty); }
set { SetValue(MyTestPropProperty, value); }
}
static TestControl()
{
MyTestPropProperty= DependencyProperty.Register(
"MyTestProp",
typeof(string),
typeof(TestControl),
new FrameworkPropertyMetadata());
}
On the XAML I"d like to be able go MyTestProp={Binding yadda yadda} but I have no way to reference it from XAML
Just add the property in the root node like so:
<Usercontrol xmlns=blablabla
MyProperty="{Binding MyViewModelProperty}">
Edit: This doesn't work because there is no XAML reference to the newly created property. Either use a <Style TargetType="TestControl"> or set the property from an outer context (the one that contains this usercontrol)

Change a textbox's text value on a UserControl from a Window in WPF

I have a UserControl (called Invoice) with a textbox (txtReferenceCode) hosted in a TabControl (myTabControl) on MainWindow. From the UserControl I call a window (SearchWindow) which contains a list of stock items. The window needs to return a string value to the textbox contained by the UserControl. I cannot access the textbox on the UserControl from the window and thus cannot pass the string value from the window to the text property.
The UserControl is an instance loaded as a new tabItem (there may be many open as content of tabitems.) I need to only affect the current tabitem instance of the UserControl.
Eg: (Button Click Event in SearchWindow)
Invoice.txtReferenceCode.Text = SearchWindow.txtReferenceCode.Text
I need a simple uncomplicated, solution preferably in VB (but I'll take C# gladly).
I got it! I am posting the solution here for any who struggle with this issue.
XAML
WPF UserControl
<UserControl x:Class="Invoice"
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"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300">
<TextBox x:Name="txtReferenceCode" Width=100 />
</UserControl>
WPF Window
<Window x:Class="SearchWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Height="300" Width="300">
<TextBox X:Name="TextToChangeTextBox" Width=100 />
</Window>
Code Behind
Add a Property to your Window
Class SearchWindow
Public ReadOnly Property TextValue
Get
Return TextToChangeTextBox.Text
End Get
End Property
...
End Class
now you can use the property in your window to pass a string to the TextBox on the UserControl.
Public Class Invoice
Private Sub SetValueToTextBox
Dim win As New SearchWindow
win.ShowDialog()
txtReferenceCode.Text = win.TextValue
End Sub
...
End Class
*
And That's it! EASY!
*
There are much better ways to go about doing this (i.e. share a viewmodel between the two windows and let binding update the textbox as needed).
But if you insist on doing it this way, try adding a public modifier to your textbox, that should let you access it like you want to.
<TextBox Name="txtReferenceCode" x:FieldModifier="public"/>

Setting content of TextBlock and text of HyperlinkButton in silverlight custom control

I am trying to create a custom control that will display a hyperlink button with some text below the link. The idea is to have urgent messages show up on a screen of a Silverlight page. From what I have read, I thought that I should be able to create a new control and then create some dependancy properties and bind the dynamic parts of the component pieces to them in order to allow me to add multiple instances of the custom control to my Silverlight project. Here is my XAML that defines the control
<UserControl
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"
mc:Ignorable="d"
x:Class="WhatsNew.UrgentStoryGridControl"
d:DesignWidth="608" d:DesignHeight="65" Background="White">
<UserControl.Resources>
<Style x:Key="WhatsNewTitleStyle" TargetType="HyperlinkButton">
Removed for Brevity
</Style>
</UserControl.Resources>
<Grid x:Name="LayoutRoot" Height="65" Margin="0" VerticalAlignment="Bottom" Background="White">
<StackPanel>
<HyperlinkButton Style="{StaticResource WhatsNewTitleStyle}" Content="{Binding linkText}" HorizontalAlignment="Left" VerticalAlignment="Top" NavigateUri="{Binding linkURI}" Foreground="Red"/>
<TextBlock Style="{StaticResource WhatsNewTextStyle}" Text="{Binding storyText}" Margin="0,13,0,0" d:LayoutOverrides="Height"/>
</StackPanel>
</Grid>
In the code behind, I have created three dependancy properties
Partial Public Class UrgentStoryGridControl
Inherits UserControl
Public Shared linkTextProperty As DependencyProperty = DependencyProperty.Register("linkText", GetType(String), GetType(UrgentStoryGridControl), New PropertyMetadata("Link Text"))
Public Shared linkURIProperty As DependencyProperty = DependencyProperty.Register("linkURI", GetType(String), GetType(UrgentStoryGridControl), New PropertyMetadata("link.html"))
Public Shared storyTextProperty As DependencyProperty = DependencyProperty.Register("storyText", GetType(String), GetType(UrgentStoryGridControl), New PropertyMetadata("Story Text"))
Public Property linkText() As String
Get
Return GetValue(linkTextProperty)
End Get
Set(ByVal value As String)
SetValue(linkTextProperty, value)
End Set
End Property
Public Property linkURI() As String
Get
Return GetValue(linkURIProperty)
End Get
Set(ByVal value As String)
SetValue(linkURIProperty, value)
End Set
End Property
Public Property storyText As String
Get
Return GetValue(storyTextProperty)
End Get
Set(ByVal value As String)
SetValue(storyTextProperty, value)
End Set
End Property
End Class
When I place this control on my Silverlight project using Expression Blend, I see the three properties listed in the Miscellaneous section of the properties window as I would expect. The values from the PropertyMetadata are populated as the default values for these properties. Here is the code from my Silverlight project where I leave the default values alone:
<local:UrgentStoryGridControl x:Name="urgentStory" Height="65" />
Here is the code where I try to set the values to something:
<local:UrgentStoryGridControl x:Name="urgentStory" Height="65" linkText="Test Link Text" linkURI="testpage.html" storyText="Sample Story Text" />
Either way I attempt to use the control, I'm not getting anything displayed when I launch the application. I figure that I'm missing something small but after having spent a lot of time today researching this, I'm not finding anything that would indicate what I'm missing or doing wrong.
You need to set the DataContext in your custom UserControl or else your bindings won't work.
In your UrgentStoryGridControl's constructor, you should be able to set Me.DataContext = Me

WPF binding set before application starts does not notify?

In theory this code should provide me with a 300x300 window with a blue background from having the window's content bound to an object of type AstRootViewModel, however this doesn't seem to be the case. I'm wondering it this is happening because I don't call astApplication.Run() until after I set the mainWindow.ViewModel property. Using snoop to check the binding I have a blank content binding and it's flagged as an error with no error information.
If it is the case that property notification does not occurr until the application Run method is called, then what would be the best way to resolve this in an MVVM friendly way?
I have the following Entry point to a WPF application:
[STAThread]
public static void Main()
{
settingsSource = LoadSettingsFile(".\\applicationSettings.xml");
astApplication = new Application();
mainWindow = new AstWindowView();
mainWindowModel = new AstRootViewModel();
dataModel = new AstDataModel(settingsSource);
mainWindow.ViewModel = mainWindowModel;
astApplication.MainWindow = mainWindow;
astApplication.Run();
}
The AstWindowView class implements the following significant code behind:
public partial class AstWindowView : Window
{
public AstRootViewModel ViewModel
{
get { return (AstRootViewModel)GetValue(ViewModelProperty); }
set { SetValue(ViewModelProperty, value); }
}
// Using a DependencyProperty as the backing store for ViewModel. This enables animation, styling, binding, etc...
public static readonly DependencyProperty ViewModelProperty =
DependencyProperty.Register("ViewModel", typeof(AstRootViewModel), typeof(Window), new UIPropertyMetadata(null));
public AstWindowView()
{
InitializeComponent();
}
}
and the following significant XAML
<Window.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="AstViewResources.xaml"/>
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Window.Resources>
<Window.Content>
<Binding Path="ViewModel" Mode="Default" UpdateSourceTrigger="PropertyChanged"/>
</Window.Content>
The AstViewResources.xaml file defines the following DataTemplate
<DataTemplate DataType="{x:Type vm:AstRootViewModel}">
<vw:AstRootView/>
</DataTemplate>
And lastly, the AstRootView XAML contains the following significant XAML:
<UserControl x:Class="SEL.MfgTestDev.AutomatedSettingsTransfer.View.AstRootView"
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 Background="#FF000CFF"/>
</UserControl>
It looks like you're not setting the DataContext for AstWindowView anywhere and your Binding has no explicit Source set. Are you seeing a binding error saying something to that effect in your debug output? Try adding after the InitializeComponent call in AstWindowView ctor (could also do it by changing the Binding in XAML):
DataContext = this;

Resources