Resource dictionary in User control - wpf

I have a user control and it uses resource dictionaries. In that user control, there is another user control which uses same resource dictionaries.what I want to know is whether wpf actually loads it twice and if yes, is there any perfomance impact. Is there any better way to do this.
Thanks in advance.

Interesting question. I was intrigged enough to investigate. It appears that WPF loads a new ResourceDirectionary (and all resources defined and the dictionary which are used) for each appearance of element.
Take a look at the following code:
ViewModel:
public class Person
{
public string name { get; set; }
public int age { get; set; }
public Person() { }
}
Resource (Dictionary1.xaml):
<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:so="clr-namespace:SO"
>
<so:Person x:Key="m" name="Methuselah" age="969" />
</ResourceDictionary>
View:
<Window
x:Class="SO.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:so="clr-namespace:SO"
Height="200" Width="300"
Title="SO Sample"
>
<Window.Resources>
<ResourceDictionary Source="Dictionary1.xaml" />
</Window.Resources>
<StackPanel DataContext={StaticResource m}>
<UserControl>
<UserControl.Resources>
<ResourceDictionary Source="Dictionary1.xaml" />
</UserControl.Resources>
<TextBlock x:Name="inner" DataContext="{StaticResource m}" Text="{Binding Path=name}" />
</UserControl>
<TextBlock x:Name="outer" Text="{Binding Path=name}" />
<Button Click="Button_Click">Change</Button>
</StackPanel>
</Window>
Put a breakpoint at the Person() constructor and notice the object is instantiated twice. Or, make Person implementing INotifyPropertyChange, and add the following code for Button_Click:
private void Button_Click( object sender, RoutedEventArgs e ) {
Person innerPerson = this.inner.DataContext as Person;
Person outerPerson = this.outer.DataContext as Person;
innerPerson.name = "inner person";
outerPerson.name = "outer person";
}
If you want to have a single instance of each resource, have the reousrces in the element of app.xaml file.

Related

Designdata via xaml in WPF VS20019

I have been struggling with trying to make sample data works out of a XAML. I have tried using this guide https://blogs.msdn.microsoft.com/wpfsldesigner/2010/06/30/sample-data-in-the-wpf-and-silverlight-designer/ and this guide https://learn.microsoft.com/en-us/windows/uwp/data-binding/displaying-data-in-the-designer to get information on the subject, but besides those pages, I haven't found any other sources with good enough information. And to try an understand this mode I made a simple WPF project to test it.
<Window x:Class="WpfApp1.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:WpfApp1" mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800" d:DataContext="{d:DesignData Source=DesignData.xaml}">
<Window.DataContext>
<local:Viewmodel/>
</Window.DataContext>
<Grid>
<TextBlock HorizontalAlignment="Left" Margin="119,104,0,0" TextWrapping="Wrap" Text="{Binding TextBlockValue}" VerticalAlignment="Top"/>
<TextBox HorizontalAlignment="Left" Height="23" Margin="255,101,0,0" TextWrapping="Wrap" Text="{Binding TextboxValue}" VerticalAlignment="Top" Width="120"/>
<Border Margin="542,71,80,223" BorderThickness="2">
<Border.BorderBrush>Black</Border.BorderBrush>
<ItemsControl ItemsSource="{Binding Persons}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding FirstName}"/>
<TextBlock Text="{Binding Lastname}"/>
</StackPanel>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</Border>
</Grid>
</Window>
This is my simple WPF window that has a textbox, textblock and a ItemsControl. It has a DataContext set with a Viewmodel and a design data DataContext. Viewmodel is as follows:
public class Viewmodel : INotifyPropertyChanged
{
public Viewmodel()
{
Persons = new ObservableCollection<Person>();
Persons.Add(new Person{FirstName = "first one", Lastname = "last one"});
Persons.Add(new Person{FirstName = "John", Lastname = "Doe"});
Persons.Add(new Person{FirstName = "Jane", Lastname = "Doe"});
TextBlockValue = "This is a textBlock";
textboxValue = "This is a textBox";
}
private string textBlockValue;
public string TextBlockValue
{
//<Omitted for readability>
}
private string textboxValue;
public string TextboxValue
{
//<Omitted for readability>
}
public ObservableCollection<Person> Persons { get; set; }
//<Omitted INotifyPropertyChanged implementation for readability>
}
public class Person : INotifyPropertyChanged
{
private string firstName;
public string FirstName
{
//<Omitted for readability>
}
private string lastname;
public string Lastname
{
//<Omitted for readability>
}
public event PropertyChangedEventHandler PropertyChanged;
//<Omitted INotifyPropertyChanged implementation for readability>
}
And my design data:
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WpfApp1">
<local:Viewmodel TextboxValue="Box Test" TextBlockValue="Block test" x:Key="Viewmodel">
<local:Viewmodel.Persons>
<local:Person Lastname="test" FirstName="test"/>
<local:Person Lastname="test" FirstName="test"/>
<local:Person Lastname="test" FirstName="test"/>
</local:Viewmodel.Persons>
</local:Viewmodel>
</ResourceDictionary>
When I add my ViewModel datacontext to the xaml I can see that the values show up in the designer. But when I assign my d:datacontext, the test data does not appear as expected. I think it is because my design data is wrong, but I cannot figure out why it is wrong.
The contents of your DesignData.xaml file should look like this, i.e. it shouldn't contain a ResourceDictionary:
<local:Viewmodel xmlns:local="clr-namespace:WpfApp1" TextboxValue="Box Test" TextBlockValue="Block test">
<local:Viewmodel.Persons>
<local:Person Lastname="test" FirstName="test"/>
<local:Person Lastname="test" FirstName="test"/>
<local:Person Lastname="test" FirstName="test"/>
</local:Viewmodel.Persons>
</local:Viewmodel>
You may also want to set the Build Action of the file to DesignData.

Binding to properties of viewmodel from datatemplate

I have the following resource dictionary:
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<DataTemplate x:Key="Sample">
<StackPanel>
<TextBlock Text="{Binding Data}" />
</StackPanel>
</DataTemplate>
</ResourceDictionary>
And in my main window:
<Window.Resources>
<ResourceDictionary Source="Dictionary.xaml" />
</Window.Resources>
<Grid>
<ContentControl ContentTemplate="{StaticResource Sample}"/>
</Grid>
Now how can I make the binding work? My window has set the datacontext to my viewmodel so I thought it would work but nothing. I dont see it applying the text.
My viewmodel has a normal property:
public string Data { get; set; } = "Hello World";
but I dont see it.
Here my mainwindow.cs
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
DataContext = new ViewModel();
}
}
Now my viewmodel:
public class ViewModel {
public string Data {get; set;} = "Hello World";
}
The entire code is kept very minimalistic for demonstration purposes.
EDIT:
Its still not working, this time to keep things simple:
public partial class MainWindow : Window
{
public string Data { get; set; } = "Hello World";
public MainWindow()
{
InitializeComponent();
DataContext = this;
}
}
Not working, I dont get it. It doesnt make sense does it? Ive set up the xaml just like explained by clemens.
You should also set the ContentControl's Content property, like shown below.
In addition to that, you should include Dictionary.xaml via ResourceDictionary.MergedDictionaries, because it would allow to have additional "local" resources.
<Window.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="Dictionary.xaml" />
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Window.Resources>
<Grid>
<ContentControl ContentTemplate="{StaticResource Sample}" Content="{Binding}"/>
</Grid>

Wpf - bestway to register a change on property

Whatsup, I'm new to wpf technology and im having trouble to find out how to popup a message on screen when there is a change on the User property. (Except that, the code works perfect).
*My goal is to register an exsisting event that takes care of it and NOT to do it in the MyData class by rewriting the 'Set'.
xaml:
<Window x:Class="WpfApplication1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WpfApplication1"
Title="MainWindow" Height="350" Width="525">
<StackPanel x:Name="MyGrid1">
<StackPanel.Resources>
<local:MyData x:Key="mySource1"
User="Arik2" />
</StackPanel.Resources>
<TextBox x:Name="target1"
Grid.Row="1"
Grid.Column="2"
Text="{Binding Source={StaticResource mySource1}, Path=User,Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>
<TextBox x:Name="target2"
Grid.Row="2"
Grid.Column="2"
Text="{Binding Source={StaticResource mySource1}, Path=User,Mode=OneWay, UpdateSourceTrigger=PropertyChanged}" />
</StackPanel>
</Window>
Thats my app code:
namespace WpfApplication1
{
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
}
public class MyData
{
public string User { get; set; }
}
}
Thank you all.
For the textboxes you have you can always open a message window in a textchanged event handler.
For example
<TextBox TextChanged="txt_TextChanged" >
Private Sub txt_TextChanged(sender As System.Object, e As System.Windows.Controls.TextChangedEventArgs)
MessageBox.Show("Value changed")
End Sub
Note this is probably what you are looking for but is triggering off the text in the box changing not the property itself. If you use anything like validation and input invalid data this event will fire but the text will not change.
I think you can use the sourcechanged event to get exactly what you want.

How do you prevent a base class property from rendering in a DataForm?

Please excuse this novice question, but I'm ramping up on Silverlight and MVVM Light. I created a view called MyView.xaml and a corresponding MyViewModel.cs.
MyView.xaml
<navigation:Page x:Class="Dashboard.Views.MyView"
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:vm="clr-namespace:Dashboard.ViewModels"
xmlns:controls="clr-namespace:Dashboard.Controls"
mc:Ignorable="d"
xmlns:navigation="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Navigation"
d:DesignWidth="640" d:DesignHeight="480"
Title="MyView Page" xmlns:toolkit="http://schemas.microsoft.com/winfx/2006/xaml/presentation/toolkit">
<navigation:Page.Resources>
<vm:MyViewModel x:Key="MyViewModel" />
</navigation:Page.Resources>
<navigation:Page.DataContext>
<Binding Source="{StaticResource MyViewModel}"/>
</navigation:Page.DataContext>
<Grid x:Name="LayoutRoot">
<StackPanel Orientation="Vertical" Style="{StaticResource LoginControlsStackPanelStyle}" HorizontalAlignment="Center" VerticalAlignment="Center">
<toolkit:DataForm Name="dataForm1" CurrentItem="{Binding}"/>
</StackPanel>
</Grid>
MyViewModel.cs
namespace Dashboard.ViewModels
{
public class MyViewModel : ViewModelBase
{
public string FirstName { get; set; }
public string LastName { get; set; }
}
}
When I run the project, my form renders the IsInDesignMode property. I obviously do not want this. How can I prevent the base class property from rendering in the dataform?
Thanks.
Andrew
If you only want to prevent one filed from showing up, you can subscribe to the AutoGeneratingField event and set the Cancel flag on the events args to true. If you want to implement your own layout, you can set the AutoGeneratingFields flag to false and provide your own templates.

WPF -- Anyone know why I can't get this binding to reference?

<StackPanel x:Name="stkWaitingPatients" Width="300" Margin="0,0,0,-3"
DataContext="{Binding Mode=OneWay, Source={StaticResource local:oPatients}}">
I'm getting StaticResource reference 'local:oPatients' was not found.
Here is the codebehind:
public partial class MainWindow : Window
{
ListBox _activeListBox;
clsPatients oPatients;
public MainWindow()
{
oPatients = new clsPatients(true);
...
To be able to address the object as a StaticResource, it needs to be in a resource dictionary. However, since you're creating the object in MainWindow's constructor, you can set the DataContext in the code-behind like so.
oPatients = new clsPatients(true);
stkWaitingPatients.DataContext = oPatients;
And then change the Binding to this:
{Binding Mode=OneWay}
This is an ok practice if you're not going to be changing the DataContext again, otherwise you'd want a more flexible solution.
Edit: You mentioned ObjectDataProvider in your comment. Here's how you'd do that. First, add an xmlns:sys to the Window for the System namespace (I'm assuming you already have one for xmlns:local):
xmlns:sys="clr-namespace:System;assembly=mscorlib"
Then you can add an ObjectDataProvider to your resource dictionary like this:
<Window.Resources>
<ObjectDataProvider
x:Key="bindingPatients"
ObjectType="{x:Type local:clsPatients}">
<ObjectDataProvider.ConstructorParameters>
<sys:Boolean>True</sys:Boolean>
</ObjectDataProvider.ConstructorParameters>
</ObjectDataProvider>
</Window.Resources>
And refer to it in a Binding with the StaticResource markup like this, using the same string we specified in the x:Key attached property we gave it in the dictionary:
{Binding Source={StaticResouce bindingPatients}, Mode=OneWay}
Edit 2: Ok, you posted more code in your answer, and now I know why it's throwing an exception during the constructor. You're attempting to do this...
lstWaitingPatients.DataContext = oPatients;
... but lstWaitingPatients doesn't actually exist until after this.InitializeComponent() finishes. InitializeComponent() loads the XAML and does a bunch of other things. Unless you really need to do something before all of that, put custom startup code after the call to InitalizeComponent() or in an event handler for Window's Loaded event.
The following sets the ItemsSource in Code Behind and correctly handles the DataBinding:
public partial class MainWindow : Window
{
public MainWindow()
{
clsPatients oPatients = new clsPatients(true);
//assuming oPatients implements IEnumerable
this.lstWaitingPatients.ItemsSource = oPatients;
And the XAML:
<ListBox x:Name="lstWaitingPatients"
IsSynchronizedWithCurrentItem="true"
ItemTemplate="{StaticResource WaitingPatientsItemTemplate}"
FontSize="21.333" Height="423.291"
ScrollViewer.VerticalScrollBarVisibility="Visible"
GotFocus="lstWaitingPatients_GotFocus"
/>
Now, I can't get this to work...I get a general Windows startup error.
Here is the codebehind with the Initializer and the class being instantiated:
public partial class MainWindow : Window
{
ListBox _activeListBox;
public MainWindow()
{
clsPatients oPatients = new clsPatients(true);
lstWaitingPatients.DataContext = oPatients;
this.InitializeComponent();
Here's the top of my XAML:
<Window
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:Orista_Charting"
xmlns:sys="clr-namespace:System;assembly=mscorlib"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
xmlns:Microsoft_Windows_Themes="clr-namespace:Microsoft.Windows.Themes;assembly=PresentationFramework.Aero"
x:Class="Orista_Charting.MainWindow"
x:Name="windowMain"
Title="Orista Chart"
Width="1024" Height="768" Topmost="True" WindowStartupLocation="CenterScreen" Activated="MainWindow_Activated" >
<Window.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="Resources/ButtonStyles.xaml"/>
<ResourceDictionary Source="Resources/OtherResources.xaml"/>
<ResourceDictionary Source="Resources/TextBlockStyles.xaml"/>
<ResourceDictionary Source="Resources/Converters.xaml"/>
</ResourceDictionary.MergedDictionaries>
Here's the pertinent XAML, as you see, I went ahead and moved the DataContext down to the ListBox from the StackPanel. This doesn't run, but it does render in Design View (however, with no data present in the ListBox):
<!-- Waiting Patients List -->
<Border BorderThickness="1,1,1,1" BorderBrush="#FF000000" Padding="10,10,10,10"
CornerRadius="10,10,10,10" Background="#FFFFFFFF" Margin="15.245,187.043,0,41.957" HorizontalAlignment="Left" >
<StackPanel x:Name="stkWaitingPatients" Width="300" Margin="0,0,0,-3">
<StackPanel Orientation="Horizontal">
<TextBlock Text="Waiting Patients:" VerticalAlignment="Center" FontSize="21.333" Margin="0,0,0,20"/>
<TextBlock HorizontalAlignment="Right" Margin="0,0,38.245,0" Width="139" Height="16"
Text="Minutes Waiting" TextWrapping="Wrap" Foreground="#FF9C2525" FontWeight="Bold" VerticalAlignment="Bottom"
TextAlignment="Right"/>
<!-- Too be implemented, this is the wait animation -->
<!--<Image x:Name="PollGif" Visibility="{Binding Loading}"
HorizontalAlignment="Left" Margin="100,0,0,0" Width="42.5" Height="42.5"
Source="Images/loading-gif-animation.gif" Stretch="Fill"/>-->
</StackPanel>
<ListBox x:Name="lstWaitingPatients"
DataContext="{Binding Mode=OneWay}" ItemsSource="{Binding Mode=OneWay}"
IsSynchronizedWithCurrentItem="true"
ItemTemplate="{StaticResource WaitingPatientsItemTemplate}"
FontSize="21.333" Height="423.291" ScrollViewer.VerticalScrollBarVisibility="Visible"
GotFocus="lstWaitingPatients_GotFocus"
/>
</StackPanel>
</Border>
Ok, but if I just take comment out the assigment line in the codebehind, it does run (albeit with no data in the listbox):
public partial class MainWindow : Window
{
ListBox _activeListBox;
public MainWindow()
{
clsPatients oPatients = new clsPatients(true);
//lstWaitingPatients.DataContext = oPatients;
THANKS!

Resources