Bind controls to a stackpanel in winui3 - c#

i'm making in a music player program, and now I need to add a page to show the playlist's detail, such as the musics and the music's artists in the playlist.I'm going to make some musics' card and add them to a stackpanel.
I have achieved this by getting this panel object and calling its add function, but can I achieve it through data binding?
This is the way i used before
<StackPanel x:Name="PanelMusics" />
PanelMusics.Children.Add(...);

You should try using ListView or ItemsRepeater instead of StackPanel.
This is a simple example how to use ListView with the help of the CommunityToolkit.Mvvm NuGet package.
MusicItem.cs
namespace MusicCardsSample;
public class MusicItem
{
public string Title { get; set; } = string.Empty;
public string Artist { get; set; } = string.Empty;
}
MainPageViewModel.cs
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
using System.Collections.ObjectModel;
namespace MusicCardsSample;
// The "CommunityTookit.Mvvm" needs this class to be "partial".
public partial class MainPageViewModel : ObservableObject
{
// The "CommunityTookit.Mvvm" creates
// an UI-interactable property "MusicItems" for you.
[ObservableProperty]
private ObservableCollection<MusicItem> musicItems = new();
// The "CommunityTookit.Mvvm" creates
// an "AddMusicItemCommand" command for you.
[RelayCommand]
private void AddMusicItem(MusicItem item)
{
musicItems.Add(item);
}
}
MainPage.xaml.cs
using Microsoft.UI.Xaml.Controls;
namespace MusicCardsSample;
public sealed partial class MainPage : Page
{
public MainPage()
{
this.InitializeComponent();
}
private MainPageViewModel ViewModel { get; } = new();
}
MainPage.xaml
<Page
x:Class="MusicCardsSample.MainPage"
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:local="using:MusicCardsSample"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"
mc:Ignorable="d">
<Grid RowDefinitions="Auto,*">
<StackPanel
Grid.Row="0"
Orientation="Horizontal">
<TextBox
x:Name="TitleTextBlock"
PlaceholderText="Title" />
<TextBox
x:Name="ArtistTextBlock"
PlaceholderText="Artist" />
<Button
Command="{x:Bind ViewModel.AddMusicItemCommand}"
Content="Add">
<Button.CommandParameter>
<local:MusicItem
Title="{x:Bind TitleTextBlock.Text, Mode=OneWay}"
Artist="{x:Bind ArtistTextBlock.Text, Mode=OneWay}" />
</Button.CommandParameter>
</Button>
</StackPanel>
<ListView
Grid.Row="1"
ItemsSource="{x:Bind ViewModel.MusicItems, Mode=OneWay}">
<ListView.ItemTemplate>
<DataTemplate x:DataType="local:MusicItem">
<!--You can design your music cards here.-->
<StackPanel Orientation="Vertical">
<TextBlock
FontSize="9"
Text="{x:Bind Artist}" />
<TextBlock Text="{x:Bind Title}" />
</StackPanel>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</Grid>
</Page>

Related

Navigate ContentControl from inside a UserControl in MVVM

I'm trying to create a sort of ShellView without using thirdparty frameworks, and I'm doing trying to do it via. ContentControls and UserControls.
I can navigate/switch the UserControls that is showed inside the ContentControls as long as the commands are fired from outside the UserControls, but nothing is happening when I move the code inside a button in a UserControl.
Currently I'm having one MainWindow.XAML, where I have one ContentControl.
This ContentControl is hosting either a LoginWindowUserControl, or a UserWindowUserControl - I want to be able switch the UserControl from within one of the UserControls.
My MainWindow.XAML looks like this:
<Window x:Class="ModelHealthApplication.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:vms="clr-namespace:ModelHealthApplication.ViewModels"
xmlns:ia="http://schemas.microsoft.com/expression/2010/interactivity"
xmlns:views="clr-namespace:ModelHealthApplication.Views.UserControls"
xmlns:local="clr-namespace:ModelHealthApplication"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<Window.Resources>
<DataTemplate DataType="{x:Type vms:CurrentWindowUserStateViewModel}">
<views:UserView />
</DataTemplate>
<DataTemplate DataType="{x:Type vms:LoginWindowViewModel}">
<views:LoginView />
</DataTemplate>
<vms:NavigationViewModel x:Key="nVm" />
</Window.Resources>
<ia:Interaction.Triggers>
<ia:EventTrigger EventName="Loaded">
<ia:InvokeCommandAction Command="{Binding Source={StaticResource nVm}, Path=OpenLoginWindowCommand}" />
</ia:EventTrigger>
</ia:Interaction.Triggers>
<Grid DataContext="{StaticResource nVm}">
<DockPanel>
<Button Content="Test" DockPanel.Dock="Left" Command="{Binding OpenUserWindowStateCommand}" />
<ContentControl x:Name="WindowUserState" Content="{Binding CurrentWindowUserState}" />
</DockPanel>
</Grid>
And my UserWindowUserControl looks like this:
<UserControl x:Class="ModelHealthApplication.Views.UserControls.UserView"
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:vms="clr-namespace:ModelHealthApplication.ViewModels"
xmlns:views="clr-namespace:ModelHealthApplication.Views.UserControls"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:ModelHealthApplication.Views.UserControls"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800">
<UserControl.Resources>
<DataTemplate DataType="{x:Type vms:MyModelsViewModel}">
<views:MyModelsView/>
</DataTemplate>
<DataTemplate DataType="{x:Type vms:MyAccountViewModel}">
<views:MyAccountView />
</DataTemplate>
<vms:NavigationViewModel x:Key="nVm" />
</UserControl.Resources>
<Grid>
<DockPanel DataContext="{StaticResource nVm}">
<Grid DockPanel.Dock="Top" Background="{StaticResource MainBlue}" Height="25">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<StackPanel Orientation="Horizontal" Grid.Column="0">
<Button Content="My Models"
HorizontalAlignment="Center"
Command="{Binding OpenMyModelsCommand}"
Style="{StaticResource NavButtonStyle}"/>
<Button Content="My Account"
HorizontalAlignment="Center"
Command="{Binding OpenMyAccountCommand}"
Style="{StaticResource NavButtonStyle}"
/>
</StackPanel>
<StackPanel Orientation="Horizontal" VerticalAlignment="Center" HorizontalAlignment="Right" Grid.Column="1" Margin="0, 0, 10, 0">
<TextBlock VerticalAlignment="Center" Foreground="White">
<Run Text="Logged in as:" FontWeight="Bold"/>
<Run Text="{Binding LoggedInAs}" d:Text="TestUser" />
</TextBlock>
<TextBlock Margin="20, 0 ,0 ,0" Text="Log Out" VerticalAlignment="Center" TextDecorations="Underline" Foreground="{StaticResource ComplenetarySecondOrange}" Background="{DynamicResource MainBlue}" Cursor="Hand">
<TextBlock.InputBindings>
<MouseBinding Command="{Binding OpenLoginWindowCommand}" MouseAction="LeftClick" />
</TextBlock.InputBindings>
</TextBlock>
</StackPanel>
</Grid>
<ContentControl x:Name="Pages" Content="{Binding SelectedViewModel}" />
</DockPanel>
</Grid>
</UserControl>
When I press the "Log Out" button/textblock I want to navigate back to the login window, but I can only do this from buttons that exist outside the UserControl.
I've read several other post similar to this, but I haven't found a solution that fits my needs - I tried using RelativeSource but without success - maybe someone can see what I'm doing wrong.
I'm using a "NavigationViewModel" to hold the commands which as I mentioned, works fine outside the UserControls:
using ModelHealthApplication.Commands;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Input;
namespace ModelHealthApplication.ViewModels
{
public class NavigationViewModel : INotifyPropertyChanged
{
public ICommand OpenMyModelsCommand { get; set; }
public ICommand OpenMyAccountCommand { get; set; }
public ICommand OpenUserWindowStateCommand { get; set; }
public ICommand OpenLoginWindowCommand { get; set; }
private object currentWindowUserState;
public object CurrentWindowUserState
{
get { return currentWindowUserState; }
set
{
currentWindowUserState = value;
OnPropertyChanged("CurrentWindowUserState");
}
}
private object selectedViewModel;
public object SelectedViewModel
{
get { return selectedViewModel; }
set
{
selectedViewModel = value;
OnPropertyChanged("SelectedViewModel");
}
}
public NavigationViewModel()
{
OpenMyModelsCommand = new OpenMyModelsCommand(this);
OpenMyAccountCommand = new OpenAccountCommand(this);
OpenUserWindowStateCommand = new OpenUserWindowStateCommand(this);
OpenLoginWindowCommand = new OpenLoginWindowCommand(this);
}
public void OpenUserWindowState(object obj)
{
CurrentWindowUserState = new CurrentWindowUserStateViewModel();
}
public void OpenLoginWindow(object obj)
{
CurrentWindowUserState = new LoginWindowViewModel();
}
public void OpenMyModels(object obj)
{
SelectedViewModel = new MyModelsViewModel();
}
public void OpenMyAccount(object obj)
{
SelectedViewModel = new MyAccountViewModel();
}
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(string propName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propName));
}
}
}
}
Ended up solving this with a great tutorial from SingletonSean on Youtube.
Posting here if anyone comes across this, and has the same issue.
Singleton Sean MVVM Navigatoin

Use UserControl in ListView UWP, Databinding Issue

I wanted to create an App to manage various Project, so i created a class to represent those Projects. In order to display them i created a user Control which should display then and later also manage the interaction between User and Project-Object.
I tried to display a list of Projects in a Page but it does not work.
I am not sure what to bind to in the Data Template, none of the ways i tried worked.
I also used the live visual tree, and i think the Property Project of ProjectViewer is correctly set, but still the Textbook does not display the Name of the Project. (But I'm not so familiar with the live visual tree so i could be wrong).
If i use the User Control as a single instance and not in a list i can set the Project property and the Name gets displayed.
I think it is an issue with the DataBinding, but i couldn't work it out.
Any help would be appreciated since this is only the very first step of the App and I'm already stuck.
Here is a minimal example i created to reproduce the problem:
I have a class Project:
using System.Collections.Generic;
using Windows.UI.Xaml;
namespace ProjectTimeTracker
{
public class Project:DependencyObject
{
public string ProjName
{
get { return (string)GetValue(ProjNameProperty); }
set { SetValue(ProjNameProperty, value); }
}
public static readonly DependencyProperty ProjNameProperty =
DependencyProperty.Register(nameof(ProjName), typeof(string), typeof(Project), new PropertyMetadata("TestName"));
public Project(){}
}
}
And i created the UserControl ProjectViewer:
namespace ProjectTimeTracker.UI
{
public sealed partial class ProjectViewer : UserControl
{
public Project Project
{
get { return (Project)GetValue(ProjectProperty); }
set { SetValue(ProjectProperty, value); }
}
public static readonly DependencyProperty ProjectProperty =
DependencyProperty.Register(nameof(Project), typeof(Project), typeof(ProjectViewer), new PropertyMetadata(null));
public ProjectViewer()
{
this.InitializeComponent();
}
}
}
which has the following xaml code:
<UserControl
x:Class="ProjectTimeTracker.UI.ProjectViewer"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:ProjectTimeTracker.UI"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
d:DesignHeight="100"
d:DesignWidth="400">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="38"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<RadioButton x:Name="RBSelector" HorizontalAlignment="Left" Margin="8,0,0,0" VerticalAlignment="Center" Grid.RowSpan="2"/>
<TextBlock x:Name="TBName" Grid.Row="0" Grid.Column="1" Text="{x:Bind Project.ProjName}" VerticalAlignment="Center" HorizontalAlignment="Left" Style="{ThemeResource TitleTextBlockStyle}" Width="110"/>
<TextBlock x:Name="TBTotalTime" Grid.Row="1" Grid.Column="1" Text="NYI" HorizontalAlignment="Left" VerticalAlignment="Center" Style="{ThemeResource CaptionTextBlockStyle}" Margin="0,-4,0,0"/>
</Grid>
</UserControl>
I try to use that User Control in the mainpage to create a list of Projects
using System.Collections.Generic;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
namespace ProjectTimeTracker
{
public sealed partial class MainPage : Page
{
public List<Project> Projects
{
get { return (List<Project>)GetValue(ProjectsProperty); }
set { SetValue(ProjectsProperty, value); }
}
public static readonly DependencyProperty ProjectsProperty =
DependencyProperty.Register(nameof(Projects), typeof(List<Project>), typeof(MainPage), new PropertyMetadata(new List<Project>()));
public MainPage()
{
this.InitializeComponent();
Projects.Add(new Project() { ProjName="Projekt1"});
list.ItemsSource = Projects;
}
}
}
The xaml looks like this:
<Page xmlns:UI="using:ProjectTimeTracker.UI"
x:Class="ProjectTimeTracker.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:ProjectTimeTracker"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<Grid>
<ListView x:Name="list" HorizontalAlignment="Center" VerticalAlignment="Center">
<ListView.ItemTemplate>
<DataTemplate x:DataType="local:Project">
<UI:ProjectViewer Project="{x:Bind }"/>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</Grid>
</Page>
Use UserControl in ListViev UWP, Databinding Issue
I have modified your code, please refer the following,
ProjectViewer.xaml.cs
public sealed partial class ProjectViewer : UserControl
{
public ProjectViewer()
{
this.InitializeComponent();
}
public Project ProjectSource
{
get { return (Project)GetValue(ProjectSourceProperty); }
set { SetValue(ProjectSourceProperty, value); }
}
public static readonly DependencyProperty ProjectSourceProperty =
DependencyProperty.Register("ProjectSource", typeof(Project), typeof(ProjectViewer), new PropertyMetadata(0, new PropertyChangedCallback(CallBack)));
private static void CallBack(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var current = d as ProjectViewer;
if (e.NewValue != e.OldValue)
{
current.TBName.Text = (e.NewValue as Project).ProjName;
}
}
}
public class Project
{
public string ProjName { get; set; }
public Project()
{
}
}
ProjectViewer.xaml
<RadioButton
x:Name="RBSelector"
Grid.RowSpan="2"
Margin="8,0,0,0"
HorizontalAlignment="Left"
VerticalAlignment="Center"
/>
<TextBlock
x:Name="TBName"
Grid.Row="0"
Grid.Column="1"
Width="110"
HorizontalAlignment="Left"
VerticalAlignment="Center"
Style="{ThemeResource TitleTextBlockStyle}"
/>
<TextBlock
x:Name="TBTotalTime"
Grid.Row="1"
Grid.Column="1"
Margin="0,-4,0,0"
HorizontalAlignment="Left"
VerticalAlignment="Center"
Style="{ThemeResource CaptionTextBlockStyle}"
Text="NYI"
/>
Usage
public sealed partial class MainPage : Page
{
public List<Project> Projects = new List<Project>();
public MainPage()
{
this.InitializeComponent();
Projects.Add(new Project() { ProjName = "Projekt1" });
list.ItemsSource = Projects;
}
public string TestString { get; set; }
}
Xaml
<ListView
x:Name="list"
HorizontalAlignment="Center"
VerticalAlignment="Center"
>
<ListView.ItemTemplate>
<DataTemplate >
<local:ProjectViewer ProjectSource="{Binding }" />
</DataTemplate>
</ListView.ItemTemplate>
</ListView>

Binding data to WPF UserControl

I have creating following GridRow as UserControl
<UserControl x:Class="Project.Telematics_Plugin.GridRow"
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" BorderBrush="LightBlue"
MaxHeight="30" MinWidth="900">
<Grid>
<StackPanel Orientation="Horizontal">
<CheckBox VerticalAlignment="Center" IsChecked="{Binding IsChecked}" />
<TextBox Width="60" Text="{Binding EventId}"/>
<TextBox Width="300" Text="{Binding MethodName}" />
<ComboBox Width="200" ItemsSource="{Binding }" />
<ComboBox Width="200"/>
<ComboBox Width="200"/>
<Button Click="OnClickEdit">
<Image Source="Images/edit.png"/>
</Button>
<Button Click="OnClickDelete">
<Image Source="Images/delete.png"/>
</Button>
</StackPanel>
</Grid>
</UserControl>
Here is the code behind
public partial class GridRow : UserControl
{
public bool IsChecked { get; set; }
public int EventId { get; set; }
public string MethodName { get; set; }
public string Level { get; set; }
public string Opcode { get; set; }
public string Task { get;set; }
public string Keyword { get; set; }
public GridRow()
{
InitializeComponent();
}
private void OnClickEdit(object sender, RoutedEventArgs e)
{
}
private void OnClickDelete(object sender, RoutedEventArgs e)
{
}
}
Now can you please tell what important thing I missed to bind properties of code behind files to UI in TwoWay Mode..
Although this is not the MVVM way..
Add an x:Name to your control and bind to the properties using ElementName:
<UserControl x:Name="MyGridRow">
<Grid>
<StackPanel Orientation="Horizontal">
<CheckBox VerticalAlignment="Center" IsChecked="{Binding IsChecked, ElementName=MyGridRow}" />
<TextBox Width="60" Text="{Binding EventId, ElementName=MyGridRow}"/>
<TextBox Width="300" Text="{Binding MethodName, ElementName=MyGridRow}" />
<ComboBox Width="200" ItemsSource="{Binding Path=., ElementName=MyGridRow}" />
<ComboBox Width="200"/>
<ComboBox Width="200"/>
<Button Click="OnClickEdit">
<Image Source="Images/edit.png"/>
</Button>
<Button Click="OnClickDelete">
<Image Source="Images/delete.png"/>
</Button>
</StackPanel>
</Grid>
</UserControl>
If you want to support updating the values, you should use DependencyProperties instead of normal properties:
public static readonly DependencyProperty IsCheckedProperty = DependencyProperty.Register("IsChecked", typeof(bool), typeof(GridRow));
public bool IsChecked
{
get { return (bool)GetValue(IsCheckedProperty); }
set { GetValue(IsCheckedProperty, value); }
}
when the DataContext where you use your usercontrol has all the properties IsChecked, EventId,MethodName ,..., then you can remove the properties from your usercontrol and all works.
but if you wanna create a "real" usercontrol then you should use DependencyProperties and bind them with the right expression within your usercontrol.
btw when you use Binding in WPF then its all about the right DataContext and the right BindingExpression

Binding ObervableCollection to ListBox

I have the following code that should display some information about ContactLists in a ListBox but there seems to be a problem with the binding as nothing is displayed. What am I missing? Would appreciate any help. Thanks!
XAML
</Window>
<Window.Resources>
<DataTemplate x:Key="ContactsTemplate">
<WrapPanel>
<TextBlock TextWrapping="Wrap"
Text="{Binding ContactListName, Mode=Default}"/>
</WrapPanel>
</DataTemplate>
</Window.Resources>
<Grid x:Name="LayoutRoot"
Background="#FFCBD5E6">
<Grid.DataContext>
<local:MyViewModel/>
</Grid.DataContext>
<ListBox x:Name="contactsList"
SelectionMode="Extended"
Margin="7,8,0,35"
ItemsSource="{Binding ContactLists}"
ItemTemplate="{DynamicResource ContactsTemplate}"
HorizontalAlignment="Left"
Width="178"
SelectionChanged="contactsList_SelectionChanged"/>
</Grid>
</Window>
ViewModel
public class MyViewModel
{
public ObservableCollection<ContactListModel> ContactLists;
public MyViewModel()
{
var data = new ContactListDataAccess();
ContactLists = data.GetContacts();
}
}
Change ContactLists to be a property for the binding to work correctly:
public class MyViewModel
{
public ObservableCollection<ContactListModel> ContactLists{get;set;}
public MyViewModel()
{
var data = new ContactListDataAccess();
ContactLists = data.GetContacts();
}
}
See here for more info.

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