WPF update textblock from different window - c#

I have a window called RolledPaper.xaml with a textblock called SequenceValue.
SequenceValue is defined in another window called CounterSettings.xaml by typing in a textbox called SequenceRequested. I would like to have SequenceValue be always in sinch with SequenceRequested.
I tryed and failed using the same datacontext for both windows.
here it is the code for RolledPaper.xaml:
<Window x:Class="Numbering3.View.RolledPaper"
WindowStyle="None"
ResizeMode="NoResize"
BorderBrush="LightGray"
BorderThickness="5"
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:Numbering3.View"
xmlns:vm="clr-namespace:Numbering3.ViewModel"
xmlns:cv="clr-namespace:Numbering3.Helper"
xmlns:vo="clr-namespace:Numbering3.ViewModel"
xmlns:sys="clr-namespace:System;assembly=mscorlib"
mc:Ignorable="d"
Title="RolledPaper" Height="750" Width="1218"
Background="{DynamicResource WindowBrush}"
DataContextChanged="Rolledpaper_DataContextChanged">
<Window.DataContext>
<vm:ViewModelRolledPaper/>
</Window.DataContext>
<TextBlock x:Name="SequenceValue" Grid.Column="3" HorizontalAlignment="Left" Height="19" Margin="72,310,0,0" TextWrapping="Wrap"
Background="White" VerticalAlignment="Top" Width="98" Text="{Binding _SequenceValueToShow.SequenceValuetoShow, Mode=TwoWay, FallbackValue=NNN, TargetNullValue=NNN, UpdateSourceTrigger=PropertyChanged}"/>
</Grid>
code behind:
public partial class RolledPaper : Window
{
public RolledPaper()
{
WindowStartupLocation = System.Windows.WindowStartupLocation.CenterScreen;
SaveKeeper.fromMain = false;
InitializeComponent();
this.MouseLeftButtonDown += delegate { this.DragMove(); };
DataContextChanged += new DependencyPropertyChangedEventHandler(Rolledpaper_DataContextChanged);
}
the CounterSetting window:
<Window x:Class="Numbering3.View.CounterSettings"
...
...
<Window.DataContext>
<ViewModelCounterSettings/>
</Window.DataContext>
<Grid Margin="0,-19,-0.4,0">
<TextBox x:Name="SequenceRequested" PreviewTextInput="SequenceValidationTextBox" MaxLength="10" HorizontalAlignment="Left" Height="23" Margin="154,324,0,0" TextWrapping="Wrap"
Text="{Binding Path=_SequenceValueToShow.SequenceValueToKeep, FallbackValue='NNN', TargetNullValue ='NNN', Mode=TwoWay, UpdateSourceTrigger=PropertyChanged }" VerticalAlignment="Top" Width="120" TextChanged="SequenceRequested_TextChanged" />
</Grid>
its code behind:
public partial class CounterSettings : Window
{
public CounterSettings()
{
InitializeComponent();
this.MouseLeftButtonDown += delegate { this.DragMove(); };
DataContextChanged += new DependencyPropertyChangedEventHandler(CounterSettings_DataContextChanged);
}
And the SequeneValue class:
public class SequenceValue : INotifyPropertyChanged
{
public string SequenceValueToKeep
{
get
{
return _sequenceValueToKeep=_sequenceProcessor.GetSequence();
}
set
{
if (_sequenceValueToKeep != value)
{
__sequenceValueToKeep = value;
RaisePropertyChanged("SequenceValueToKeep");
}
}
}
private void RaisePropertyChanged(string prop)
{
if (PropertyChanged != null)
{ PropertyChanged(this, new PropertyChangedEventArgs(prop)); }
}
public event PropertyChangedEventHandler PropertyChanged;
}
}
I need to put a value in the textbox of countersetting =>string Sequencevalue.SequenceValueToKeep is updated => textblock in RolledPaper window shows Sequencevalue.SequenceValueToKeep .
Thank you.

Solutio 1:
You can use one Static ViewModel to get the to to know each other.
in App.xaml
<Application.Resources>
...
<MainViewModel x:Key="mainViewModel" />
</Application.Resources>
and then in CounterSetting.xaml
<Window otherProperties=...
DataContext="{Binding ViewModelCounterSettings, Source={StaticResource mainViewModel}}"/>
and in RolledPaper.xaml
<Window otherProperties=...
DataContext="{Binding ViewModelRolledPaper, Source={StaticResource mainViewModel}}"/>
The MainViewModel does inits your two ViewModel and makes them accessible as properties and also gives the the same instance of SequenceValue
Solution 2:
Create an object of SequenceValue as an static resource, like
<Application.Resources>
...
<SequenceValue x:Key="sequenceValue"/>
</Application.Resources>
and set it as the DataContext of your TextBox and TextBlock
<TextBox DataContext="{StaticResource sequenceValue}"
Text="{Binding SequenceValueToKeep, FallbackValue='NNN', TargetNullValue='NNN', Mode=TwoWay, UpdateSourceTrigger=PropertyChanged }"
... />
<TextBlock DataContext="{StaticResource sequenceValue}"
Text="{Binding SequenceValuetoShow, FallbackValue=NNN, TargetNullValue=NNN, UpdateSourceTrigger=PropertyChanged}"

Related

Wpf Databinding with ViewModel as property

to keep my MainWindow.xaml.cs clean I tried to outsource everything to a ViewModel class and then bind my view to properties of my ViewModel property
However using this extra layer doesn't seem to work. My List- and Textbox just stay empty. Any way to get this method working?
MainWindow class:
public partial class MainWindow : Window, INotifyPropertyChanged
{
public ViewModel ViewModel { get; set; }
public MainWindow()
{
InitializeComponent();
ViewModel = new ViewModel();
}
}
ViewModel class:
public class ViewModel : INotifyPropertyChanged
{
private string m_oneLineOnly;
private ObservableCollection<string> m_sampleLines;
public ObservableCollection<string> SampleLines
{
get => m_sampleLines;
set
{
m_sampleLines = value;
RaisePropertyChanged(nameof(SampleLines));
}
}
public string OneLineOnly
{
get => m_oneLineOnly;
set
{
m_oneLineOnly = value;
RaisePropertyChanged(nameof(OneLineOnly));
}
}
public ViewModel()
{
SampleLines = new ObservableCollection<string>(new List<string>()
{
"Gedanken",
"Zeit",
"Sand"
});
OneLineOnly = "Hello World";
}
public event PropertyChangedEventHandler PropertyChanged = delegate { };
private void RaisePropertyChanged(string propertyName)
{
var handlers = PropertyChanged;
handlers(this, new PropertyChangedEventArgs(propertyName));
}
}
MainWindow XAML
<Window x:Class="DataContext_Test_Project.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:DataContext_Test_Project"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800"
x:Name="Window">
<Grid>
<ListBox
HorizontalAlignment="Left"
Height="100"
Margin="334,132,0,0"
VerticalAlignment="Top"
Width="100"
ItemsSource="{Binding ElementName=Window, Path=ViewModel.SampleLines}"/>
<TextBox
HorizontalAlignment="Left"
Height="23"
Margin="184,166,0,0"
TextWrapping="Wrap"
VerticalAlignment="Top"
Width="120"
DataContext="{Binding ElementName=Window, Path=ViewModel}"
Text="{Binding Path=OneLineOnly}"/>
</Grid>
You can also define the DataContext in code behind:
public partial class MainWindow : Window
{
public ViewModel ViewModel { get; set; }
public MainWindow()
{
InitializeComponent();
ViewModel = new ViewModel();
DataContext = ViewModel;
}
}
And clean your XAML:
<Grid>
<ListBox
HorizontalAlignment="Left"
Height="100"
Margin="334,132,0,0"
VerticalAlignment="Top"
Width="100"
ItemsSource="{Binding SampleLines}"/>
<TextBox
HorizontalAlignment="Left"
Height="23"
Margin="184,166,0,0"
TextWrapping="Wrap"
VerticalAlignment="Top"
Width="120"
Text="{Binding OneLineOnly}"/>
</Grid>
In order to make Bindings like
ItemsSource="{Binding ElementName=Window, Path=ViewModel.SampleLines}"
DataContext="{Binding ElementName=Window, Path=ViewModel}"
work, you have two options. Either make the ViewModel property fire a property change notification, or simply initialize it before the XAML is parsed, i.e. before InitializeComponent() is called:
Like this:
public partial class MainWindow : Window
{
public ViewModel ViewModel { get; }
public MainWindow()
{
ViewModel = new ViewModel();
InitializeComponent();
}
}
Or simpler:
public partial class MainWindow : Window
{
public ViewModel ViewModel { get; } = new ViewModel();
public MainWindow()
{
InitializeComponent();
}
}
Remove the following line from xaml.cs file : ViewModel = new ViewModel();
Use the following code for Xaml file:
<Window x:Class="DataContext_Test_Project.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:DataContext_Test_Project"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800"
x:Name="Window" DataContext="{DynamicResource ViewModel}">
<Window.Resources>
<local:ViewModel x:Key="ViewModel"/>
</Window.Resources>
<Grid>
<ListBox
HorizontalAlignment="Left"
Height="100"
Margin="334,132,0,0"
VerticalAlignment="Top"
Width="100"
ItemsSource="{Binding Path=SampleLines}"/>
<TextBox
HorizontalAlignment="Left"
Height="23"
Margin="184,166,0,0"
TextWrapping="Wrap"
VerticalAlignment="Top"
Width="120"
Text="{Binding Path=OneLineOnly}"/>
</Grid>
</Window>

Data binding to the output of a method

I'm trying to display the output of a method in a WPF TextBox. I'm just trying a simple attempt, to print a single string 3 in a TextBox.
I'm trying to do it the following way, using an ObjectDataProvider:
<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"
xmlns:system="clr-namespace:System;assembly=mscorlib"
Title="MainWindow" Height="350" Width="525">
<Window.Resources>
<ObjectDataProvider x:Key="dataprovider" ObjectType="{x:Type system:String}" MethodName="GetValue">
</ObjectDataProvider>
</Window.Resources>
<Grid>
<TextBox Text="{Binding Source={StaticResource dataprovider}, Mode=OneWay}" HorizontalAlignment="Left" Height="23" Margin="201,168,0,0" TextWrapping="Wrap" VerticalAlignment="Top" Width="120"/>
</Grid>
</Window>
And here's my code behind:
namespace WpfApplication1
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
public string GetValue()
{
return "3";
}
}
}
I'm getting no output. the TextBox is just blank. Where am I going wrong?
Instead of ObjectDataProvider create a property like this:
public string GetMethod
{
get
{
return GetValue();
}
}
And:
public MainWindow()
{
InitializeComponent();
DataContext = this;
}
Then in the XAML remove ObjectDataProvider and just:
<TextBox Text="{Binding GetMethod, Mode=OneWay}" HorizontalAlignment="Left"
Height="23" Margin="201,168,0,0" TextWrapping="Wrap"
VerticalAlignment="Top" Width="120"/>

Dependency property not binding to view

what im tryin to do is populate a combobox based upon the selected item of a treeview. as depending on what node is chosen will populate the combobox with a different list of report levels.
the combobox is within a user control and i am trying to bind to a dependency property ReportLevel i have in my MainViewModel. if i set the value of the combobox its fine but i want to be able to update it whenever a user choses a different node on the tree.
here is my xaml
<UserControl x:Name="this"
x:Class="RTHM.ComboboxControl1"
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"
xmlns:local ="clr-namespace:RTHM.ViewModels"
d:DesignHeight="300" d:DesignWidth="300">
<Grid>
<StackPanel HorizontalAlignment="Center" MinHeight="221.904" Margin="12,12,42,0" VerticalAlignment="Top" MaxWidth="246.226" Width="246">
<WrapPanel HorizontalAlignment="Left" MinHeight="224.072" VerticalAlignment="Top" MinWidth="246.13">
<TextBox HorizontalAlignment="Left" MinHeight="104.489" Margin="10,289.95,0,0" TextWrapping="Wrap" Text="{Binding ReportDescription, Mode=TwoWay}" VerticalAlignment="Top" MinWidth="245.124"/>
<TextBox MinHeight="23" TextWrapping="Wrap" Text="Report Level:" MinWidth="120" BorderThickness="1" Margin="0,0,5,5"/>
<ComboBox MinWidth="120" Margin="0,0,0,5" MinHeight="23"
ItemsSource="{Binding ElementName=this, Path=ReportLevel,Mode=TwoWay}"
IsSynchronizedWithCurrentItem="True"
DisplayMemberPath="Name"
SelectedItem="{Binding Path=SelectedLevel, Mode=TwoWay}"
IsEnabled="{Binding IsEnabled}"/>
my code behind
public partial class ComboboxControl1 : UserControl
{
public ComboboxControl1()
{
InitializeComponent();
DataContext = new MainViewModel();
in my MainViewModel i have this
#region DependencyProperties
public static readonly DependencyProperty LevelProperty =
DependencyProperty.Register("ReportLevel",typeof(ObservableCollection<ReportLevel>),typeof(MainViewModel));
public ObservableCollection<ReportLevel> ReportLevel
{
get
{
return (ObservableCollection<ReportLevel>)GetValue(LevelProperty);
}
set
{
SetValue(LevelProperty, value);
}
}
and have a method which sets the value of this like
ReportLevel = c.GetUserReportLevel();
i have tried various things with the ItemSource of the combobox but without success.
Any help on the matter would be appreciated
Thanks,
Marty
EDIT: Have updated my code but still no luck with this matter, any ideas?
Xaml
<UserControl x:Class="RTHM.ComboboxControl1"
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"
xmlns:local ="clr-namespace:RTHM.ViewModels"
d:DesignHeight="300" d:DesignWidth="300">
<Grid>
<StackPanel HorizontalAlignment="Center" MinHeight="221.904" Margin="12,12,42,0" VerticalAlignment="Top" MaxWidth="246.226" Width="246">
<WrapPanel HorizontalAlignment="Left" MinHeight="224.072" VerticalAlignment="Top" MinWidth="246.13">
<TextBox HorizontalAlignment="Left" MinHeight="104.489" Margin="10,289.95,0,0" TextWrapping="Wrap" Text="{Binding ReportDescription, Mode=TwoWay}" VerticalAlignment="Top" MinWidth="245.124"/>
<TextBox MinHeight="23" TextWrapping="Wrap" Text="Report Level:" MinWidth="120" BorderThickness="1" Margin="0,0,5,5"/>
<ComboBox MinWidth="120" Margin="0,0,0,5" MinHeight="23"
ItemsSource="{Binding Path=Levels}"
IsSynchronizedWithCurrentItem="True"
DisplayMemberPath="Name"
SelectedItem="{Binding Path=SelectedLevel, Mode=TwoWay}"
IsEnabled="{Binding IsEnabled}"/>
and in MainViewModel
private ObservableCollection<ReportLevel> _levels;
public ObservableCollection<ReportLevel> Levels
{
get
{
return _levels;
}
set
{
_levels = value;
NotifyPropertyChanged("Levels");
}
}
my MainViewModel inherits from a base class which has INotifyProperty changed and an implementation
public class ViewModelBase : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected void NotifyPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
The binding
ItemsSource="{Binding ElementName=this, Path=ReportLevel, Mode=TwoWay}"
binds to a ReportLevel property in your UserControl. There is however no such property, because it is in the DataContext of the UserControl. The binding should look like shown below. Please also note that a two-way binding on the ItemsSource property doesn't make sense, as the control never sets that property.
ItemsSource="{Binding Path=ReportLevel}"
That said, you do usually not have dependency properties in view models. Instad, the view model classes should implement the INotifyPropertyChanged interface.
You also have to actually fire the PropertyChanged event when the ReportLevel property changes:
private ObservableCollection<ReportLevel> reportLevel;
public ObservableCollection<ReportLevel> ReportLevel
{
get { return reportLevel; }
set
{
reportLevel = value;
OnPropertyChanged("ReportLevel");
}
}

WPF Data binding with ResourceDictionary MVVM

I'm trying to bind a View with a ViewModel within ResourceDictionary but it does not work.
The application is very simple window with 2 textboxes. When I type text to textbox1, atutomatically, textbox2 must get the same text. Of course my textboxes from the View have to be binded to my properties in ViewModel.
I'm new to WPF and the way I started to bind Views and ViewModels was in the codebehind of a View:
DataContext = new MyViewModel();
Now I'm trying to achieve a cleaner separation. My code is
App.xaml:
<Application x:Class="NavigationCleanBinding.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
StartupUri="/Views/MainWindowView.xaml">
<Application.Resources>
<ResourceDictionary Source="MainResourceDictionary.xaml" />
</Application.Resources>
</Application>
MainResourceDictionary.xaml:
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xamlpresentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:Views="clr-namespace:NavigationCleanBinding.Views"
xmlns:ViewModels="clr-namespace:NavigationCleanBinding.ViewModels">
<DataTemplate DataType="{x:Type ViewModels:MainWindowViewModel}">
<Views:MainWindowView />
</DataTemplate>
</ResourceDictionary>
MainWindowView.xaml:
<Window x:Class="NavigationCleanBinding.Views.MainWindowView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">
<Grid>
<TextBox Height="23" HorizontalAlignment="Left" Margin="61,14,0,0"
Name="textBox1" VerticalAlignment="Top" Width="120"
Text="{Binding TestData, Mode=TwoWay,
UpdateSourceTrigger=PropertyChanged}"/>
<Label Content="Test:" Height="28" HorizontalAlignment="Left" Margin="12,12,0,0"
Name="label1" VerticalAlignment="Top" Width="43" />
<Label Content="Result:" Height="28" HorizontalAlignment="Left" Margin="10,46,0,0"
Name="label2" VerticalAlignment="Top" />
<TextBox Height="23" HorizontalAlignment="Left" Margin="61,48,0,0"
Name="textBox2" VerticalAlignment="Top" Width="120"
Text="{Binding TestData, Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}"/>
</Grid>
</Window>
MainWindowViewModel:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace NavigationCleanBinding.ViewModels
{
class MainWindowViewModel
{
private String _testData;
public String TestData
{
get { return _testData; }
set { _testData = value; }
}
private MainWindowViewModel()
{
_testData = null;
}
}
}
UPDATE:
I changed property TestData to this:
public String TestData
{
get { return _testData; }
set
{
_testData = value;
OnPropertyChanged("TestData");
}
}
And implemened the INotifyPropertyChanged like this:
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(String propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
so user1064519 was on the right track:
the View needs to be a UserControl, not a Window, as it is hosted in the MainWindow
the ViewModel needs to be loaded into the MainWindow, this is what triggers the DataTemplate to be discovered and loaded.
<Window x:Class="WpfTemplateBootstrap.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WpfTemplateBootstrap"
Title="MainWindow" Height="350" Width="525">
<ContentControl>
<ContentControl.Content>
<local:MainWindowViewModel />
</ContentControl.Content>
</ContentControl>
After that you should be up and running.
I have posted an in-depth example here: wpf bootstrapping datatemplates--the chicken and the egg
DataTemplate sholudnt contain window, it can contains any kind of control.
DataTemplate :
<DataTemplate DataType="{x:Type ViewModels:MainWindowViewModel}">
<Views:MainWindowView />
</DataTemplate>
UserControl :
<UserControl x:Class="NavigationCleanBinding.Views.MainView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Height="350" Width="525">
<Grid>
<TextBox Height="23" HorizontalAlignment="Left" Margin="61,14,0,0"
Name="textBox1" VerticalAlignment="Top" Width="120"
Text="{Binding TestData, Mode=TwoWay,
UpdateSourceTrigger=PropertyChanged}"/>
<Label Content="Test:" Height="28" HorizontalAlignment="Left" Margin="12,12,0,0"
Name="label1" VerticalAlignment="Top" Width="43" />
<Label Content="Result:" Height="28" HorizontalAlignment="Left" Margin="10,46,0,0"
Name="label2" VerticalAlignment="Top" />
<TextBox Height="23" HorizontalAlignment="Left" Margin="61,48,0,0"
Name="textBox2" VerticalAlignment="Top" Width="120"
Text="{Binding TestData, Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}"/>
</Grid>
</UserControl>
Window :
<Window x:Class="NavigationCleanBinding.Views.MainWindowView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">
<ContentControl Content={Binding}/>
</Window>
Your ViewModel must implement the interface INotifyPropertyChanged and raise a PropertyChanged event when any binded property value changes, so that your view can know that changes ocurred.

What is the best way to reuse blocks of XAML?

I've got many user controls like this:
PageManageCustomers.xaml.cs:
public partial class PageManageCustomers : BasePage
{
...
}
which inherit from:
PageBase.cs:
public class BasePage : UserControl, INotifyPropertyChanged
{
...
}
Since PageBase.cs has no accompanying XAML file, I have to put the XAML that it refers to in each of the user controls which inherit it, e.g. the following block is repeated in every XAML file of every control that inherits PageBase:
<DataTemplate x:Key="manageAreaCellTemplate">
<StackPanel Orientation="Horizontal">
<TextBlock Style="{DynamicResource ManageLinkStyle}"
Tag="{Binding Id}" Text="Delete" MouseDown="System_Delete_Click"/>
<TextBlock Text=" "/>
<TextBlock Style="{DynamicResource ManageLinkStyle}"
Tag="{Binding Id}" Text="Edit" MouseDown="System_Edit_Click"/>
</StackPanel>
</DataTemplate>
I'm trying to put this block into a resource file but can't get the syntax right, it says:
'ResourceDictionary' root element
requires a x:Class attribute to
support event handlers in the XAML
file. Either remove the event handler
for the MouseDown event, or add a
x:Class attribute to the root element.
Or perhaps I could read these blocks in with XamlReader` somehow?
How can I put this repeated block of code in one place so that it is not repeated in every XAML file that inherits BagePage?
Here is a reproducable example of this problem:
Window1.xaml:
<Window x:Class="TestXamlPage8283.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">
<StackPanel x:Name="MainContent"/>
</Window>
Window1.xaml.cs:
using System.Windows;
namespace TestXamlPage8283
{
public partial class Window1 : Window
{
public Window1()
{
InitializeComponent();
Page1 page1 = new Page1();
MainContent.Children.Add(page1);
Page2 page2 = new Page2();
MainContent.Children.Add(page2);
}
}
}
Page1.xaml:
<local:BasePage x:Class="TestXamlPage8283.Page1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:TestXamlPage8283"
Height="40" Width="300">
<StackPanel>
<TextBlock Text="{Binding PageTitle}"
FontSize="14"
FontWeight="Bold"/>
<TextBlock Text="This is XAML that is specific to page one." />
</StackPanel>
</local:BasePage>
Page1.xaml.cs:
namespace TestXamlPage8283
{
public partial class Page1 : BasePage
{
public Page1()
{
InitializeComponent();
PageTitle = "Page One";
}
}
}
Page2.xaml:
<local:BasePage x:Class="TestXamlPage8283.Page2"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:TestXamlPage8283"
Height="40" Width="300">
<StackPanel>
<TextBlock Text="{Binding PageTitle}"
FontSize="14"
FontWeight="Bold"/>
<TextBlock Text="This is XAML that is specific to page two." />
</StackPanel>
</local:BasePage>
Page2.xaml.cs:
namespace TestXamlPage8283
{
public partial class Page2 : BasePage
{
public Page2()
{
InitializeComponent();
PageTitle = "Page Two";
}
}
}
BasePage.cs:
using System.Windows.Controls;
using System.ComponentModel;
namespace TestXamlPage8283
{
public class BasePage : UserControl, INotifyPropertyChanged
{
#region ViewModelProperty: PageTitle
private string _pageTitle;
public string PageTitle
{
get
{
return _pageTitle;
}
set
{
_pageTitle = value;
OnPropertyChanged("PageTitle");
}
}
#endregion
public BasePage()
{
DataContext = this;
}
#region INotifiedProperty Block
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
#endregion
}
}
How do I take this block
<TextBlock Text="{Binding PageTitle}"
FontSize="14"
FontWeight="Bold"/>
out of Page1.xaml and Page2.xaml and put it in one place so I can refer to it from Page1.xaml and Page2.xaml? (so that when I want to change FontSize=14 to FontSize=16, I just have to change it in one place)
Use resource dictionaries - add a MyDictionary.xaml file to your project, setting its Build Action to "Page":
<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<DataTemplate x:Key="manageAreaCellTemplate">
<StackPanel Orientation="Horizontal">
<TextBlock Style="{DynamicResource ManageLinkStyle}"
Tag="{Binding Id}" Text="Delete" MouseDown="System_Delete_Click"/>
<TextBlock Text=" "/>
<TextBlock Style="{DynamicResource ManageLinkStyle}"
Tag="{Binding Id}" Text="Edit" MouseDown="System_Edit_Click"/>
</StackPanel>
</DataTemplate>
</ResourceDictionary>
Then in other XAML files you refer to it with:
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="MyDictionary.xaml"/>
</ResourceDictionary.MergedDictionaries>
... some other local resources ...
</ResourceDictionary>
and refer to your resource as Template={StaticResource manageAreaCellTemplate}.

Categories

Resources