I have a View that contains a lot of input parameters. Each parameter consists of a label (name of the parameter) a textbox for its value and another label for its units (eg. kNm).
The Control displays in a horizontal Stackpanel.
When changing the Text (ToWay Binding) of the Textbox the underlying Property (eg. DesignLife) does not update anymore, since I switched from straight WPF (OnPropertyChanged) to MVVM Light (RaisePropertyChanged).
Here are some relevant pieces of code:
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
namespace xxxx.Views.CustomControls
{
public class InputParameter : ContentControl
{
static InputParameter()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(InputParameter), new FrameworkPropertyMetadata(typeof(InputParameter)));
}
public override void OnApplyTemplate()
{
base.OnApplyTemplate();
}
// some properties left out
public static readonly DependencyProperty TextProperty =
DependencyProperty.Register("Text", typeof(string), typeof(InputParameter));
public string Text
{
get => (string)GetValue(TextProperty);
set => SetValue(TextProperty, value);
}
}
}
<UserControl
x:Class="xxxx.Views.MainUserControls.Column"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:cc="clr-namespace:xxxx.Views.CustomControls"
xmlns:cf="clr-namespace:xxxx.Model.Config"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:tt="clr-namespace:xxxx.Properties"
DataContext="{Binding Main.Calc, Source={StaticResource Locator}}"
mc:Ignorable="d">
<GroupBox Margin="3">
<StackPanel Orientation="Vertical">
<!-- Stuff left out -->
<cc:InputParameter
NameContent="Design life"
Text="{Binding DesignLife, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged, ValidatesOnDataErrors=True, StringFormat=N0}"
UnitContent="years" />
<!-- Stuff left out -->
</StackPanel>
</GroupBox>
</UserControl>
<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:cc="clr-namespace:CoCa.Views.CustomControls"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d">
<Style x:Key="InputParm" TargetType="{x:Type cc:InputParameter}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type cc:InputParameter}">
<StackPanel Style="{StaticResource SPanel}">
<Label
x:Name="PART_NameLabel"
Width="130"
HorizontalContentAlignment="Left"
Content="{TemplateBinding NameContent}"
Foreground="{TemplateBinding Foreground}"
Style="{StaticResource LabelStyle1}"
ToolTip="{TemplateBinding ToolTip}" />
<TextBox
x:Name="PART_TextBox"
Width="70"
Background="{TemplateBinding TbBackground}"
Foreground="{TemplateBinding Foreground}"
Style="{StaticResource Tb}"
Text="{TemplateBinding Property=Text}" />
<Label
x:Name="PART_UnitLabel"
Content="{TemplateBinding UnitContent}"
Foreground="{Binding ElementName=PART_NameLabel, Path=Foreground}"
Style="{StaticResource Unit}" />
</StackPanel>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ResourceDictionary>
private int _designLife;
public int DesignLife
{
get => _designLife;
set
{
Set<int>(() => DesignLife, ref _designLife, value, true);
}
}
I suspect that RaisePropertyChanged does not get called because it is not used anywhere in the class InputParameter's Text DependencyProperty. (Maybe under the hood it does?)
Questions:
1: Is the way I made InputParameter the way to go?
2: Is my suspicion correct?
3 a: If so, How do I get RaisePropertyChanged called?
b: If not: what could be the problem?
Related
I have the following XAML code:
<Window x:Class="LinkButton.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:LinkButton"
mc:Ignorable="d"
Title="MainWindow" Height="350" Width="525"
DataContext="{StaticResource MainWindowVM}">
<Window.Resources>
<Style TargetType="TextBlock">
<Setter Property="Margin" Value="10" />
</Style>
</Window.Resources>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<TextBlock Grid.Row="0" Grid.Column="0" Text="ddk" />
<TextBlock Grid.Row="0" Grid.Column="1" >
<Hyperlink Command="{Binding Link}"
CommandParameter="{Binding}"
Foreground="Blue" >
<Hyperlink.Inlines>
<TextBlock>
<TextBlock.Style>
<Style>
<Setter Property="TextBlock.Text" Value="{Binding Description01.Header}" />
</Style>
</TextBlock.Style>
</TextBlock>
</Hyperlink.Inlines>
</Hyperlink>
</TextBlock>
<TextBlock Grid.Row="1" Grid.Column="0" Text="dde" />
<TextBlock Grid.Row="1" Grid.Column="1">
<Hyperlink Command="{Binding Link}"
CommandParameter="{Binding}"
Foreground="Blue" >
<Hyperlink.Inlines>
<TextBlock>
<TextBlock.Style>
<Style>
<Setter Property="TextBlock.Text" Value="{Binding Description11.Header}" />
</Style>
</TextBlock.Style>
</TextBlock>
</Hyperlink.Inlines>
</Hyperlink>
</TextBlock>
</Grid>
</Window>
And the C# Code code:
public class TestCommand : ICommand
{
public delegate void ICommandOnExecute(object parameter);
public delegate bool ICommandOnCanExecute(object parameter);
private ICommandOnExecute _execute;
private ICommandOnCanExecute _canExecute;
public TestCommand(ICommandOnExecute onExecuteMethod, ICommandOnCanExecute onCanExecuteMethod)
{
_execute = onExecuteMethod;
_canExecute = onCanExecuteMethod;
}
#region ICommand Members
public event EventHandler CanExecuteChanged
{
add { CommandManager.RequerySuggested += value; }
remove { CommandManager.RequerySuggested -= value; }
}
public bool CanExecute(object parameter)
{
return _canExecute.Invoke(parameter);
}
public void Execute(object parameter)
{
_execute.Invoke(parameter);
}
#endregion
}
public class LongDescription
{
public string Header { get; }
public string Description { get; }
public LongDescription(string header, string description)
{
Header = header;
Description = description;
}
}
public class MainWindowVM
{
public ICommand Link => new TestCommand(ExecuteCommand1, CanExecuteCommand1);
public LongDescription Description11 => new LongDescription("cell11", "result cell11");
public LongDescription Description01 => new LongDescription("cell01", "result cell01");
public bool CanExecuteCommand1(object parameter)
{
return true;
}
public void ExecuteCommand1(object parameter)
{
MessageBox.Show("Executing command 1");
}
}
It is clear that I have duplicated code in XAML ( <Hyperlink.Inlines> etc). I want to refactor it so that the code duplication is eliminated. For that I am thinking of defining the style <Hyperlink.Inlines> in ResourceDictionary and then bind it to appropriate properties in MainWindowVM.
But I am unsure how to do it, any ideas?
You can easily move the Style in a ResourceDictionary like this
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<!-- Key is required to identify the Style -->
<Style x:Key="Bind01" TargetType="TextBlock">
<Setter Property="Text" Value="{Binding Description01.Header}" />
</Style>
<Style x:Key="Bind11" TargetType="TextBlock">
<Setter Property="Text" Value="{Binding Description11.Header}" />
</Style>
</ResourceDictionary>
And merge the Dictionary in your Window to use the Style
Merge
<Window.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="YourDictionaryHere"/>
</ResourceDictionary.MergedDictionaries>
<Style TargetType="TextBlock">
<Setter Property="Margin" Value="10" />
</Style>
</ResourceDictionary>
</Window.Resources>
Use
<TextBox Style="{DynamicResource Bind01}"/>
Simplification
Instead of putting the variable Binding in a Style (or Dictionary), i suggest to write the variable Bindings directly into the Control and define the rest as Style.
More Specific: The following Markup displays a bound string as a Hyperlink which executes a ICommand when clicked.
<TextBlock>
<Hyperlink Command="{Binding Link}"
CommandParameter="{Binding}"
Foreground="Blue" >
<Hyperlink.Inlines>
<TextBlock>
<TextBlock.Style>
<Style>
<Setter Property="TextBlock.Text" Value="{Binding Description11.Header}" />
</Style>
</TextBlock.Style>
</TextBlock>
</Hyperlink.Inlines>
</Hyperlink>
</TextBlock>
We could instead define a Style for a Button which looks (and does) the same, but the variable Binding can be set directly via Content.
Button Style
<Style x:Key="LinkStyle" TargetType="Button">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Button">
<TextBlock>
<Hyperlink Command="{Binding Link}" CommandParameter="{Binding}">
<Run Text="{TemplateBinding Content}"/>
</Hyperlink>
</TextBlock>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
Apply style to Elements in Grid (replace TextBlock with styled Buttons)
<TextBlock Grid.Row="0" Grid.Column="0" Text="ddk" />
<Button Grid.Row="1" Grid.Column="1"
Content="{Binding Description01.Header}"
Style="{DynamicResource LinkStyle}">
<TextBlock Grid.Row="1" Grid.Column="0" Text="dde" />
<Button Grid.Row="1" Grid.Column="1"
Content="{Binding Description11.Header}"
Style="{DynamicResource LinkStyle}">
Screens (dashed Lines are Gridlines)
Edit
To set the Command of the Hyperlink we use the Command Property of the Button to set the Binding. Therefore we must add a TemplateBinding in our Style. Replace the "Hard Coded" Command with a TemplateBinding to the Button Command. Do the same for the Commandparameter.
<Hyperlink Command="{TemplateBinding Command}"
CommandParameter="{Templatebinding Commandparameter}"
Foreground="Blue" >
And set the Command and the CommandParameter in the styled Button
<TextBlock Grid.Row="0" Grid.Column="0" Text="ddk" />
<Button Grid.Row="1" Grid.Column="1"
Content="{Binding Description01.Header}"
Command="{Binding YOURCOMMANDHERE}"
CommandParameter="{YOURPARAMETER}"
Style="{DynamicResource LinkStyle}">
<TextBlock Grid.Row="1" Grid.Column="0" Text="dde" />
<Button Grid.Row="1" Grid.Column="1"
Content="{Binding Description11.Header}"
Command="{Binding YOUROTHERCOMMANDHERE}"
CommandParameter="{YOUROTHERPARAMETER}"
Style="{DynamicResource LinkStyle}">
I have a button with the image and textblock.
Buttons are created dynamically based on the values from the database.
Now for a particular value text is present and no image is there I want to show that text in the center of the button (horizontally and vertically), but it is not working.
Please find the xaml below:
<ItemsControl ItemsSource="{Binding CategoriesList}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Button Width="100" Margin="5" HorizontalContentAlignment="Center" VerticalContentAlignment="Center">
<Button.Template>
<ControlTemplate>
<Border CornerRadius="10" Background="Maroon">
<StackPanel Orientation="Vertical">
<Image Source="{Binding CategoryImagePath}" Height="50"></Image>
<TextBlock Text="{Binding CategoryName}" Height="20" HorizontalAlignment="Center" VerticalAlignment="Center"></TextBlock>
</StackPanel>
</Border>
</ControlTemplate>
</Button.Template>
</Button>
</DataTemplate>
</ItemsControl.ItemTemplate>
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>
If no image is available I want to show only the text on the button but it should be centered.
If there is Image then I make the Image and text both displayed but when the Image is not available the text is getting displayed but it is not in the center It moves to the top portion of the button.
You can use a DataTemplateSelector to select different templates depending on whether you have an image. Such a selector might look like this:
public sealed class ButtonTemplateSelector : DataTemplateSelector
{
/// <summary>
/// Gets or sets the <see cref="DataTemplate"/> to use when we have an image.
/// The value is set in XAML.
/// </summary>
public DataTemplate ImageTemplate { get; set; }
/// <summary>
/// Gets or sets the <see cref="DataTemplate"/> to use when we don't have an image.
/// The value is set in XAML.
/// </summary>
public DataTemplate NoImageTemplate { get; set; }
public override DataTemplate SelectTemplate(object item, DependencyObject container)
{
Category category = item as Category;
if (category != null)
{
return category.CategoryImagePath == null ? NoImageTemplate : ImageTemplate;
}
return base.SelectTemplate(item, container);
}
}
I'm assuming a model object something like this:
public class Category
{
public string CategoryImagePath { get; set; }
public string CategoryName { get; set; }
}
Create and initialize a ButtonTemplateSelector resource in your XAML, then reference it from your ItemsControl:
<Window
x:Class="WPF.MainWindow"
x:Name="self"
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:wpf="clr-namespace:WPF"
mc:Ignorable="d"
Title="MainWindow"
Height="350"
Width="525">
<Grid>
<Grid.Resources>
<wpf:ButtonTemplateSelector x:Key="ButtonTemplateSelector">
<wpf:ButtonTemplateSelector.ImageTemplate>
<DataTemplate DataType="wpf:Category">
<Button
Width="100"
Margin="5"
HorizontalContentAlignment="Center"
VerticalContentAlignment="Center">
<Button.Template>
<ControlTemplate>
<Border CornerRadius="10" Background="Maroon">
<StackPanel Orientation="Vertical">
<Image
Source="{Binding CategoryImagePath}"
Height="50" />
<TextBlock
Foreground="White"
Text="{Binding CategoryName}"
Height="20"
HorizontalAlignment="Center"
VerticalAlignment="Center" />
</StackPanel>
</Border>
</ControlTemplate>
</Button.Template>
</Button>
</DataTemplate>
</wpf:ButtonTemplateSelector.ImageTemplate>
<wpf:ButtonTemplateSelector.NoImageTemplate>
<DataTemplate DataType="wpf:Category">
<Button
Width="100"
Margin="5"
HorizontalContentAlignment="Center"
VerticalContentAlignment="Center">
<Button.Template>
<ControlTemplate>
<Border
CornerRadius="10"
Background="Maroon"
Height="70">
<TextBlock
Foreground="White"
Text="{Binding CategoryName}"
Height="20"
HorizontalAlignment="Center"
VerticalAlignment="Center" />
</Border>
</ControlTemplate>
</Button.Template>
</Button>
</DataTemplate>
</wpf:ButtonTemplateSelector.NoImageTemplate>
</wpf:ButtonTemplateSelector>
</Grid.Resources>
<ItemsControl
DataContext="{Binding ElementName=self}"
ItemsSource="{Binding CategoriesList}"
ItemTemplateSelector="{StaticResource ButtonTemplateSelector}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>
</Grid>
</Window>
For completeness, the code-behind for the window:
public partial class MainWindow
{
public MainWindow()
{
InitializeComponent();
}
public IEnumerable<Category> CategoriesList { get; } = new List<Category>
{
new Category { CategoryName = "First", CategoryImagePath = "/Assets/Square.bmp" },
new Category { CategoryName = "Second", CategoryImagePath = null },
};
}
This shows up as follows, which I think is what you're asking:
I wanted to bind data inside a DataTemplate that is stored in ViewModel. I've tried several ways but did not succeed and the solutions for WPF doesn't seems to work on WinRT like AncestorType Property of RelativeSource.
<Page.DataContext>
<local:ViewModel x:Name="ViewModel" />
</Page.DataContext>
<StackPanel Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<ListView ItemsSource="{x:Bind ViewModel.names}">
<ListView.ItemTemplate>
<DataTemplate x:DataType="local:mydatatype">
<StackPanel>
<TextBlock Text="{Binding Name}"/>
<!--Here I want a TextBlock to show the number-->
</StackPanel>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</StackPanel>
Here is the ViewModel
public class ViewModel
{
public int Number = 42;
public List<mydatatype> names = new List<mydatatype>();
public ViewModel()
{
names.Add(new mydatatype("name1"));
names.Add(new mydatatype("name2"));
}
}
public class mydatatype
{
public string Name { get; set; }
public mydatatype(string name)
{
this.Name = name;
}
}
You can access the DataTemplate of other objects by giving them a name and then referencing this. Using this technique you should be able to access its DataContext to bind to the viewmodel directly, even from within a DataTemplate
<Page x:Name="PageRoot">
<Page.DataContext>
<local:ViewModel x:Name="ViewModel" />
</Page.DataContext>
<StackPanel Background="{ThemeResource ApplicationPageBackgroundThemeBrush}" x:Name="MainPanel">
<ListView ItemsSource="{x:Bind ViewModel.names}">
<ListView.ItemTemplate>
<DataTemplate x:DataType="local:mydatatype">
<StackPanel>
<TextBlock Text="{Binding Name}"/>
<TextBlock Text="{Binding DataContext.Name, ElementName=PageRoot}"/>
</StackPanel>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</StackPanel>
</Page>
I'm new to windows development. I'm making a simple windows app which has a few pages and each page has a similar layout in XAML. Like this:
Each page is separated into 3 sections. A will have a title, B is where the content will be inserted and C is for other stuff. My question is: what is the simplest way to build a general layout template so that I can reuse it for every page? And is it possible?
For example, I have a MainTemplate.xaml file with this layout:
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition/>
<RowDefinition/>
</Grid.RowDefinitions>
</Grid>
And then my Page1.xaml will load MainTemplate so I don't have to copy and paste the same layout into every page of mine. I've tried looking online but the solutions are going way over my head. I was wondering if there's a simple way to do this like with webpages.Thanks a lot.
One feasible way to do this is using UserControl with ContentPresenter. For example:
Add a UserControl named MainTemplate. In the XAML, set the layout with ContentPresenter and bind it to the DependencyProperty defined in code-behind.
<UserControl x:Class="UWPTest.MainTemplate"
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:UWPTest"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
d:DesignHeight="300"
d:DesignWidth="400"
mc:Ignorable="d">
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>
<ContentPresenter Content="{x:Bind Title}" />
<ContentPresenter Grid.Row="1" Content="{x:Bind Main}" />
<ContentPresenter Grid.Row="2" Content="{x:Bind Stuff}" />
</Grid>
</UserControl>
In the code-behind, set the DependencyProperty so that we can use them to set the content in other pages.
public sealed partial class MainTemplate : UserControl
{
public MainTemplate()
{
this.InitializeComponent();
}
public static readonly DependencyProperty TitleProperty = DependencyProperty.Register("Title", typeof(object), typeof(MainTemplate), new PropertyMetadata(null));
public object Title
{
get { return GetValue(TitleProperty); }
set { SetValue(TitleProperty, value); }
}
public static readonly DependencyProperty MainProperty = DependencyProperty.Register("Main", typeof(object), typeof(MainTemplate), new PropertyMetadata(null));
public object Main
{
get { return GetValue(MainProperty); }
set { SetValue(MainProperty, value); }
}
public static readonly DependencyProperty StuffProperty = DependencyProperty.Register("Stuff", typeof(object), typeof(MainTemplate), new PropertyMetadata(null));
public object Stuff
{
get { return GetValue(StuffProperty); }
set { SetValue(StuffProperty, value); }
}
}
After this, we can use the UserControl in other pages to reuse the general layout. For example, using it in "MainPage.xaml":
<Page x:Class="UWPTest.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:UWPTest"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d">
<local:MainTemplate>
<local:MainTemplate.Title>
<Grid Background="Red">
<TextBlock HorizontalAlignment="Center" VerticalAlignment="Center" FontSize="60">A</TextBlock>
</Grid>
</local:MainTemplate.Title>
<local:MainTemplate.Main>
<Grid Background="Green">
<TextBlock HorizontalAlignment="Center" VerticalAlignment="Center" FontSize="60">B</TextBlock>
</Grid>
</local:MainTemplate.Main>
<local:MainTemplate.Stuff>
<Grid Background="Yellow">
<TextBlock HorizontalAlignment="Center" VerticalAlignment="Center" FontSize="60">C</TextBlock>
</Grid>
</local:MainTemplate.Stuff>
</local:MainTemplate>
</Page>
Then the "MainPage" will look like follwoing:
I am playing around with an example of Expander Control from Windows Phone Toolkit (I am using it for wp7).
When I load up the stripped down version everything seems expanded. When I click on Customer Pizza or 2 nothing happens. I would like the sub stuff to be collapsed but I don't know how.
<phone:PhoneApplicationPage
x:Class="ExpanderViewSample.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:phone="clr-namespace:Microsoft.Phone.Controls;assembly=Microsoft.Phone"
xmlns:shell="clr-namespace:Microsoft.Phone.Shell;assembly=Microsoft.Phone"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:toolkit="clr-namespace:Microsoft.Phone.Controls;assembly=Microsoft.Phone.Controls.Toolkit"
mc:Ignorable="d" d:DesignWidth="480" d:DesignHeight="768"
FontFamily="{StaticResource PhoneFontFamilyNormal}"
FontSize="{StaticResource PhoneFontSizeNormal}"
Foreground="{StaticResource PhoneForegroundBrush}"
SupportedOrientations="Portrait" Orientation="Portrait"
shell:SystemTray.IsVisible="True">
<phone:PhoneApplicationPage.Resources>
<toolkit:RelativeTimeConverter x:Key="RelativeTimeConverter"/>
<DataTemplate x:Key="CustomHeaderTemplate">
<StackPanel Orientation="Horizontal">
<Image Source="{Binding Image}" Stretch="None"/>
<TextBlock Text="{Binding Name}"
FontSize="{StaticResource PhoneFontSizeExtraLarge}"
FontFamily="{StaticResource PhoneFontFamilySemiLight}"/>
</StackPanel>
</DataTemplate>
<DataTemplate x:Key="CustomExpanderTemplate">
<StackPanel Orientation="Horizontal">
<Image Source="{Binding Image}" Stretch="None"/>
<TextBlock Foreground="{StaticResource PhoneSubtleBrush}" VerticalAlignment="Center"
FontSize="{StaticResource PhoneFontSizeNormal}">
<TextBlock.Text>
<Binding Path="DateAdded" Converter="{StaticResource RelativeTimeConverter}" StringFormat="Date added: {0}" />
</TextBlock.Text>
</TextBlock>
</StackPanel>
</DataTemplate>
</phone:PhoneApplicationPage.Resources>
<!--LayoutRoot is the root grid where all page content is placed-->
<Grid x:Name="LayoutRoot" Background="Transparent">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<!--TitlePanel contains the name of the application and page title-->
<StackPanel x:Name="TitlePanel" Grid.Row="0" Margin="12,17,0,28">
<TextBlock x:Name="ApplicationTitle" Text="WindowsPhoneGeek.com" Style="{StaticResource PhoneTextNormalStyle}"/>
<TextBlock x:Name="PageTitle" Text="ExpanderViewSample" Margin="9,-7,0,0" Style="{StaticResource PhoneTextTitle2Style}"/>
</StackPanel>
<!--ContentPanel - place additional content here-->
<Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<ListBox Grid.Row="0" x:Name="listBox">
<ListBox.ItemContainerStyle>
<Style TargetType="ListBoxItem">
<Setter Property="HorizontalContentAlignment" Value="Stretch"/>
</Style>
</ListBox.ItemContainerStyle>
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel/>
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
<ListBox.ItemTemplate>
<DataTemplate>
<toolkit:ExpanderView Header="{Binding}" Expander="{Binding}"
IsExpanded="False"
HeaderTemplate="{StaticResource CustomHeaderTemplate}"
ExpanderTemplate="{StaticResource CustomExpanderTemplate}"></toolkit:ExpanderView>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Grid>
</Grid>
</phone:PhoneApplicationPage>
public partial class MainPage : PhoneApplicationPage
{
// Constructor
public MainPage()
{
InitializeComponent();
List<CustomPizza> customPizzas = new List<CustomPizza>()
{
new CustomPizza()
{
Name = "Custom Pizza 1",
IsExpanded = false,
DateAdded = new DateTime(2010, 7, 8),
Image="Images/pizza1.png"
},
new CustomPizza() { Name = "Custom Pizza 2", DateAdded = new DateTime(2011, 2, 10), Image="Images/pizza2.png"}
};
this.listBox.ItemsSource = customPizzas;
// Important properties:
// IsExpanded
// Header
// Expander
// ItemsSource
// HeaderTemplate
// ExpanderTemplate
// ItemTemplate
// NonExpandableHeader
// IsNonExpandable
// NonExpandableHeaderTemplate
}
}
public class CustomPizza : INotifyPropertyChanged
{
private bool isExpanded;
public string Image
{
get;
set;
}
public string Name
{
get;
set;
}
public DateTime DateAdded
{
get;
set;
}
public bool IsExpanded
{
get
{
return this.isExpanded;
}
set
{
if (this.isExpanded != value)
{
this.isExpanded = value;
this.OnPropertyChanged("IsExpanded");
}
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string propertyName)
{
PropertyChangedEventHandler handler = this.PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
}
I also don't get what this really is for
ExpanderView Header="{Binding}" Expander="{Binding}"
I don't get what "binding" is referring too. It just seems to know which data to use but I don't know how it knows.
To change the expanded state of the expander view you can do the following
-register for tap event and add binding to IsExpanded (this will bind to the IsExpanded property of CustomPizza)
<toolkit:ExpanderView Header="{Binding}" Expander="{Binding}"
IsExpanded="{Binding IsExpanded}"
HeaderTemplate="{StaticResource CustomHeaderTemplate}"
ExpanderTemplate="{StaticResource CustomExpanderTemplate}"
Tap="expander_OnTap"></toolkit:ExpanderView>
-in the tap event switch the IsExpanded flag of the CustomPizza:
private void expander_OnTap(object sender, System.Windows.Input.GestureEventArgs e)
{
ExpanderView expander = sender as ExpanderView;
CustomPizza customPizza = expander.DataContext as CustomPizza;
customPizza.IsExpanded = !customPizza.IsExpanded;
}
Regarding the question about ExpanderView Header="{Binding}" Expander="{Binding}", when you set (or bind) the ItemsSource property of an ItemsControl to a list (ListBox is inheriting from a ItemsControl), the DataTemplate inside the ItemTemplate will be automatically set to each individual item. For example here you are setting it to a List of CustomPizza so each ItemTemplate DataContext will be a CustomPiza. So the ExpanderView will have the CustomPizza as DataContext. {Binding} will just pass the DataContext so like this whatever is inside the HEaderTemplate will get the same DataContext (CustomPizza ). If you had put {Binding Image} then the HeaderTemplate will just have the Image string as DataContext.