I have a listbox in which I render data using multiple datatemplates, and I use a datatemplate selector to route data to the appropriate template.
Each template has it's own layout using a grid. The first column of every grid inside the template is a textblock and I want them aligned to left. The next item is another textblock which should be aligned towards the maximum width of the first textblock (something similar to a data entry form). I'm using Grid.IsSharedSizeScope for this, but not able to achieve this. Below is my code:
<Page xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Page.Resources>
<DataTemplate x:Key="DefaultTemplate">
<Border BorderThickness="1" CornerRadius="3" BorderBrush="LightGray">
<TextBlock Text="{Binding Name}"></TextBlock>
</Border>
</DataTemplate>
<DataTemplate x:Key="ShortFieldTemplate">
<Grid Grid.IsSharedSizeScope="True">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" SharedSizeGroup="A"/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<TextBlock Text="Age:" Grid.Column="0"></TextBlock>
<TextBlock Text="{Binding Age}" Grid.Column="1"></TextBlock>
</Grid>
</DataTemplate>
<DataTemplate x:Key="LongFieldTemplate">
<Grid Grid.IsSharedSizeScope="True">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" SharedSizeGroup="A"/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<TextBlock Text="This should be the name:" Grid.Column="0"></TextBlock>
<TextBlock Text="{Binding Name}" Grid.Column="1"></TextBlock>
</Grid>
</DataTemplate>
<GridSplitterTextTrim:MyFirstTemplateSelector x:Key="MyFirstTemplateSelector"
DefaultDataTemplate="{StaticResource DefaultTemplate}"
ShortFieldDataTemplate="{StaticResource ShortFieldTemplate}"
LongFieldDataTemplate="{StaticResource LongFieldTemplate}"
/>
</Page.Resources>
<Grid x:Name="LayoutRoot" Grid.IsSharedSizeScope="True">
<Grid Grid.Column="2" Background="Green" >
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition/>
<RowDefinition/>
</Grid.RowDefinitions>
<ListBox ItemsSource="{Binding Items}" Grid.Row="0" Grid.Column="0" x:Name="listbox" ItemTemplateSelector="{StaticResource MyFirstTemplateSelector}">
</ListBox>
</Grid>
</Grid>
</Page>
..and my object model
public class ShortField : INotifyPropertyChanged
{
private string _name;
public string Age
{
get { return _name; }
set
{
_name = value;
InvokePropertyChanged(new PropertyChangedEventArgs("Age"));
}
}
private string _currentValue;
public string CurrentValue
{
get { return _currentValue; }
set
{
_currentValue = value;
InvokePropertyChanged(new PropertyChangedEventArgs("CurrentValue"));
}
}
public event PropertyChangedEventHandler PropertyChanged;
public void InvokePropertyChanged(PropertyChangedEventArgs e)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null) handler(this, e);
}
public static List<ShortField> GetShortFields()
{
return new List<ShortField>()
{
new ShortField() {Age = "10"},
new ShortField() {Age = "21"},
};
}
}
How do I get this alignment right? I thought Grid.IsSharedScope should do the trick. Is that correct or is there any other way?
Thanks in advance,
-Mike
try to set
<ListBox Grid.IsSharedSizeScope="True"
Related
I need to change the color of a rectangle in a GridView when the item is selected.
Unselected item
Selected item
My Main Page in XAML.
<Page
x:Class="GridViewWithSelectedItem.Views.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:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:views="using:GridViewWithSelectedItem.Views"
Style="{StaticResource PageStyle}"
mc:Ignorable="d">
<Page.Resources>
<DataTemplate x:Key="TileTemplate" x:DataType="views:Article">
<Border BorderThickness="2,2,2,2" BorderBrush="#FF868484" Margin="3,3,3,3" HorizontalAlignment="Stretch" MaxWidth="600" MinWidth="525">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="10"/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition Height="60"/>
<RowDefinition/>
</Grid.RowDefinitions>
<Rectangle Fill="Black" Grid.RowSpan="3"/>
<StackPanel Orientation="Horizontal" Grid.Row="0" Grid.Column="1">
<TextBlock FontSize="20" Margin="0,0,5,0" TextWrapping="Wrap" Foreground="DarkBlue" FontWeight="Bold" Text="{x:Bind Number}"/>
<TextBlock FontSize="20" TextWrapping="Wrap" Foreground="DarkBlue" Text="{x:Bind Title}" FontWeight="Bold"/>
</StackPanel>
<TextBlock Grid.Row="1" Grid.Column="1" FontSize="20" Text="{x:Bind Description}" TextWrapping="WrapWholeWords"/>
<StackPanel Background="LightBlue" Padding="5,0,0,0" Grid.Row="2" Grid.Column="1">
<StackPanel Orientation="Horizontal">
<TextBlock FontSize="20" Margin="0,0,5,0" TextWrapping="Wrap">Date :</TextBlock>
<TextBlock Foreground="Red" FontSize="20" TextWrapping="Wrap" Text="{x:Bind Date}" FontWeight="Bold"/>
</StackPanel>
</StackPanel>
</Grid>
</Border>
</DataTemplate>
</Page.Resources>
<Grid x:Name="ContentArea" Margin="{StaticResource MediumLeftRightMargin}">
<Grid Background="White" VerticalAlignment="Top" HorizontalAlignment="Stretch" Margin="0,25,0,0">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="600"/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="60"/>
<RowDefinition Height="800"/>
</Grid.RowDefinitions>
<TextBlock Text="The Guardian" FontSize="35" FontWeight="Bold" Grid.ColumnSpan="2" HorizontalAlignment="Center"/>
<GridView
BorderThickness="2,2,0,2" BorderBrush="#FF868484"
MinWidth="600"
Grid.Column="0"
Grid.Row="1"
Padding="5,5,5,5"
HorizontalAlignment="Center"
CanDragItems="False"
IsItemClickEnabled="true"
IsTapEnabled="False"
IsSwipeEnabled="False"
ItemsSource="{x:Bind Articles}"
ItemTemplate="{StaticResource TileTemplate}"
SelectedItem="{x:Bind Mode=TwoWay, Path=SelectedArticle}"
/>
<RelativePanel Grid.Row="1" Grid.Column="1" Background="WhiteSmoke" BorderThickness="2" BorderBrush="#FF868484" Padding="10">
<Grid RelativePanel.AlignLeftWithPanel="True" RelativePanel.AlignRightWithPanel="True">
<Grid VerticalAlignment="Top">
<Grid.ColumnDefinitions>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition/>
</Grid.RowDefinitions>
<StackPanel Orientation="Horizontal" Grid.Row="0">
<TextBlock FontSize="20" Margin="0,0,5,0" TextWrapping="Wrap" Foreground="DarkBlue" FontWeight="Bold" Text="{x:Bind Path=SelectedArticle.Number, Mode=OneWay}"/>
<TextBlock FontSize="20" TextWrapping="Wrap" Foreground="DarkBlue" Text="{x:Bind Path=SelectedArticle.Title, Mode=OneWay}" FontWeight="Bold"/>
</StackPanel>
<TextBlock Grid.Row="1" FontSize="20" Text="{x:Bind Path=SelectedArticle.Description, Mode=OneWay}" TextWrapping="WrapWholeWords"/>
</Grid>
</Grid>
<RelativePanel Background="LightBlue" Padding="5,0,0,0" RelativePanel.AlignBottomWithPanel="True" RelativePanel.AlignLeftWithPanel="True" RelativePanel.AlignRightWithPanel="True">
<StackPanel Orientation="Horizontal">
<TextBlock FontSize="20" Margin="0,0,5,0" TextWrapping="Wrap">Date :</TextBlock>
<TextBlock Foreground="Red" FontSize="20" TextWrapping="Wrap" Text="{x:Bind Path=SelectedArticle.Date, Mode=OneWay}" FontWeight="Bold"/>
</StackPanel>
</RelativePanel>
</RelativePanel>
</Grid>
</Grid>
</Page>
And the class of my XAML Page.
using System;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Runtime.CompilerServices;
using Windows.UI.Xaml.Controls;
namespace GridViewWithSelectedItem.Views
{
public sealed partial class MainPage : Page, INotifyPropertyChanged
{
public ObservableCollection<Article> Articles;
private Article _selectedArticle;
public Article SelectedArticle
{
get { return _selectedArticle; }
set { Set(ref _selectedArticle, value); }
}
public MainPage()
{
InitializeComponent();
Articles = new ObservableCollection<Article>();
Articles.Add(new Article(0, "Uighurs", "Being young' leads to detention in China's Xinjiang region", DateTime.Parse("09/12/2020")));
Articles.Add(new Article(1, "Brexit", "Chances of Brexit deal hang on Boris Johnson and Ursula von der Leyen dinner", DateTime.Parse("09/12/2020")));
Articles.Add(new Article(2, "Environment", "Secretive ‘gold rush’ for deep-sea mining dominated by handful of firms", DateTime.Parse("09/12/2020")));
Articles.Add(new Article(3, "Juukan Gerge induiry", "Juukan Gorge inquiry: Rio Tinto's decision to blow up Indigenous rock shelters 'inexcusable'", DateTime.Parse("09/12/2020")));
Articles.Add(new Article(4, "Australia", "British journalist uncovered Australian woman's alleged plan to kill parents on dark web, police say", DateTime.Parse("09/12/2020")));
Articles.Add(new Article(5, "Coronavirus", "Nine out of 10 in poor nations to miss out on inoculation as west buys up Covid vaccines", DateTime.Parse("09/12/2020")));
}
public event PropertyChangedEventHandler PropertyChanged;
private void Set<T>(ref T storage, T value, [CallerMemberName]string propertyName = null)
{
if (Equals(storage, value))
{
return;
}
storage = value;
OnPropertyChanged(propertyName);
}
private void OnPropertyChanged(string propertyName) => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
public class Article
{
public int Number { get; set; }
public string Title { get; set; }
public string Description { get; set; }
public DateTime Date { get; set; }
public Article(int number, string title, string description, DateTime date)
{
Number = number;
Title = title;
Description = description;
Date = Date;
}
}
}
I see AutomationProperty.name can help me but i don't understand how to use it.
I found a way to change the color of a selected item in my class but i need to recreate the collection and i cost lot of resources. I think its possible to make it in XAML code.
Edit: I made a simple exemple of my code.
OneDrive link
You could add a Brush property into the Article class, and bind the Brush property to Rectangle.Fill property of your DataTemplate to change the color when an item of GridView control is selected.
Please check the following code:
In DataTemplate of your xaml file:
<Rectangle x:Name="rectangle" Fill="{x:Bind Brush}" Grid.RowSpan="3"/>
……
<GridView …… SelectionChanged="GridView_SelectionChanged" />
In code-behind:
public class Article
{
……
public SolidColorBrush Brush { get; set; }
public Article(int number, string title, string description, DateTime date)
{
……
Brush = new SolidColorBrush(Colors.Black);
}
}
Add a _preSelectedArticle property to save the previous item in MainPage class:
private Article _preSelectedArticle;
Change the value of Brush property of selected item and previous selected item:
private void GridView_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
if(_preSelectedArticle==null)
{
SelectedArticle.Brush.Color = Colors.Green;
_preSelectedArticle = SelectedArticle;
}
if (_preSelectedArticle!=null&&_preSelectedArticle!= SelectedArticle)
{
_preSelectedArticle.Brush.Color = Colors.Black;
SelectedArticle.Brush.Color = Colors.Green;
_preSelectedArticle = SelectedArticle;
}
}
I want to make layout with table on page. Right now I have:
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<ScrollViewer Grid.Row="0" Margin="10,0,10,0">
<ListView
x:Name="NewsList"
HorizontalAlignment="Center">
<ListView.ItemTemplate>
<DataTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<TextBlock Margin="15" Grid.Column="0" FontSize="25" Text="{Binding news_time}"/>
<TextBlock Margin="15" Grid.Column="1" FontSize="25" Text="{Binding title}"/>
<TextBlock Margin="15" Grid.Column="2" FontSize="25" Text="{Binding news_text}"/>
</Grid>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</ScrollViewer>
</Grid>
I want to make binding for my ObservableCollection objects to add Grid rows dynamically to StackPanel tags. Here is my .cs file.
public class News
{
public int id;
public string news_time;
public string title;
public string news_text;
}
public sealed partial class TermsAndNews : Page
{
public ObservableCollection<News> newsCollection { get; set; } =
new ObservableCollection<News>();
// ...
// Method called on initialization
private async void GetData()
{
var httpClient = new HttpClient();
var responsetNews = await httpClient.GetAsync("http://localhost:3000/news");
responsetNews.EnsureSuccessStatusCode();
string jsonNews = await responsetNews.Content.ReadAsStringAsync();
this.newsCollection = JsonConvert.DeserializeObject<ObservableCollection<News>>(jsonNews);
NewsList.ItemsSource = newsCollection;
}
}
After running the code, nothing is displayed. Any ideas ?
Try changing
this.newsCollection = .....
to
ObservableCollection<News> tempobcol = ...
then iterating through and adding to newsCollection
Foreach (News n in tempobcol)
{ newsCollection.Add(n); }
and finally, removing
NewsList.ItemsSource = newsColleciton;
in order to add it to the XAML
<ListView x:Name:"NewsList" ItemsSource:="{Binding newsCollection}"...../>
I have a Grid control that is proportioned using star e.g.
<Grid.ColumnDefinitions>
<ColumnDefinition Width="50*" />
<ColumnDefinition Width="100*" />
<ColumnDefinition Width="50*" />
</Grid.ColumnDefinitions>
However putting a long TextBlock in the grid that overflows causes the proportions to be upset. e.g.
<TextBlock Text="Foo" Grid.Column="0" />
<TextBlock Text="Some long text here which overflows" Grid.Column="1" />
<TextBlock Text="Foo" Grid.Column="2" />
This causes the central column to be more than double the other two. How do I maintain the specified proportions? Is it possible to clip the content?
I have set TextTrimming="CharacterEllipsis" on the TextBlocks but no luck.
Edit
Crucially it seems, the Grid is inside a DataTemplate, paste the following to observe the behaviour,
<!-- FallbackValue is just a quick hack to get some rows to show at design-time -->
<ListBox ItemsSource="{Binding Foo, FallbackValue=1234}"
HorizontalContentAlignment="Stretch">
<ListBox.ItemTemplate>
<DataTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="50*" />
<ColumnDefinition Width="100*" />
<ColumnDefinition Width="50*" />
</Grid.ColumnDefinitions>
<TextBlock Text="Foo" Grid.Column="0" />
<TextBlock Text="Some long text here which overflows" TextTrimming="CharacterEllipsis" Grid.Column="1" />
<TextBlock Text="Foo" Grid.Column="2" />
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
The reason why this is important is that I have another Grid as a sibling of the ListBox which displays the 'headers' for the columns shown in the ListBox as follows,
<Grid>
... Headers and column definitions here
</Grid>
<ListBox ...>
<ListBox.ItemTemplate>
<DataTemplate>
<Grid>
... Matching column definitions here
</Grid>
</DateTemplate>
</ListBox.ItemTemplate>
</ListBox>
and so it is important that the columns match up.
I have tried to bind the ColumnDefinitions inside the DataTemplate to the external Grid ColumnDefinitions but I cannot get easily a binding reference to it.
This is one of the most annoying problems with WPF. Since the available space yielded to the templated grid is infinite, the actual content will take as much space as it wants.
The simplest way is to fix a certain width to the Grid, but that solves only the situations where there's no resizing.
Whereas you want to stretch the ListBox size (width, in the specific), unfortunately I guess that there's no any better solution other than a custom converter.
Here is my solution:
<Window.Resources>
<local:MyConv x:Key="cv1" />
</Window.Resources>
<Grid>
<ListBox
ItemsSource="{Binding Foo, FallbackValue=1234}"
HorizontalContentAlignment="Stretch"
>
<ListBox.ItemTemplate>
<DataTemplate>
<Grid Width="{Binding Path=ActualWidth, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=ListBox}, Converter={StaticResource cv1}}">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="50*" />
<ColumnDefinition Width="100*" />
<ColumnDefinition Width="50*" />
</Grid.ColumnDefinitions>
<TextBlock Text="Foo" Grid.Column="0" />
<TextBlock Text="Some long text here which overflows" TextTrimming="CharacterEllipsis" Grid.Column="1" />
<TextBlock Text="Foo" Grid.Column="2" />
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Grid>
And the converter:
class MyConv : IValueConverter
{
public object Convert(
object value,
Type targetType,
object parameter,
System.Globalization.CultureInfo culture
)
{
return (double)value - 30.0;
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
}
Even though this is an old post I'm adding my findings as they might be relevant for other people reading this post.
I had a similar issue (my * columns weren't dividing the width evenly as expected anymore, they were just sizing based on the content).
The root cause here was that I had a ListView with an ItemsSource linked to a List. The ListView in WPF contains a ScrollViewer and a ScrollViewer doesn't have a fixed width.
Without a fixed width a Grid can't properly determine what width to give to a * column and switches to a different sizing method.
Solution
I now use an ItemsControl which doesn't contain a ScrollViewer and thus the Width is known allowing the Grid to properly size it's columns.
For more details on how exactly the Grid handles it's sizing I suggest you decompile the Grid class and have a look at the following method:
protected override Size MeasureOverride(Size constraint)
This is my MainWindow.xaml from my test application (comment out the ListView to see the difference in behaviour):
<Window x:Class="WPFSO.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:wpfso="clr-namespace:WPFSO"
Title="MainWindow" Height="150" Width="525">
<Window.DataContext>
<wpfso:SharedSizeScopeViewModel />
</Window.DataContext>
<Window.Resources>
<DataTemplate DataType="{x:Type wpfso:TestViewModel}">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" x:Name="SecondColumn" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" x:Name="FourthColumn" />
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="0" Text="{Binding Name}" />
<TextBlock Grid.Column="1" Background="LightGray" Text="{Binding Name2}"/>
<TextBlock Grid.Column="2" Text="{Binding Name3}"/>
<TextBlock Grid.Column="3" Background="Orange" Text="{Binding Name4}"/>
<!--<TextBlock Grid.Column="1" Background="Blue" HorizontalAlignment="Stretch" />
<TextBlock Grid.Column="3" Background="Orange" HorizontalAlignment="Stretch" />-->
</Grid>
</DataTemplate>
<DataTemplate x:Key="MainDataTemplate" DataType="wpfso:SharedSizeScopeViewModel" >
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<CheckBox Grid.Row="0" Grid.ColumnSpan="4" HorizontalAlignment="Left" FlowDirection="RightToLeft" Margin="0,0,0,25">
<TextBlock FlowDirection="LeftToRight" Text="Show differences" Style="{StaticResource LabelStyle}" />
</CheckBox>
<TextBlock Grid.Row="1" Grid.Column="0" Text="PropertyName" Style="{StaticResource LabelStyle}" />
<TextBlock Grid.Row="1" Grid.Column="1" Text="Previous value" Style="{StaticResource LabelStyle}" />
<TextBlock Grid.Row="1" Grid.Column="3" Text="Current value" Style="{StaticResource LabelStyle}" />
<ListView Grid.Row="2" Grid.Column="0" Grid.ColumnSpan="4" ItemsSource="{Binding Entries}" HorizontalAlignment="Stretch" Margin="0" HorizontalContentAlignment="Stretch"/>
</Grid>
</DataTemplate>
</Window.Resources>
<Grid Name="RootGrid">
<ItemsControl ItemsSource="{Binding Entries}" />
<!--<ListView ItemsSource="{Binding Entries}" />-->
</Grid>
</Window>
The ViewModels used during this test:
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Runtime.CompilerServices;
namespace WPFSO
{
public class SharedSizeScopeViewModel : INotifyPropertyChanged
{
public SharedSizeScopeViewModel()
{
var testEntries = new ObservableCollection<TestViewModel>();
testEntries.Add(new TestViewModel
{
Name = "Test",
Name2 = "Looooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong test",
Name3 = "Short test",
Name4 = "Nothing"
});
Entries = testEntries;
}
private ObservableCollection<TestViewModel> _entries;
public ObservableCollection<TestViewModel> Entries
{
get { return _entries; }
set
{
_entries = value;
OnPropertyChanged();
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
}
First viewmodel
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Runtime.CompilerServices;
namespace WPFSO
{
public class SharedSizeScopeViewModel : INotifyPropertyChanged
{
public SharedSizeScopeViewModel()
{
var testEntries = new ObservableCollection<TestViewModel>();
testEntries.Add(new TestViewModel
{
Name = "Test",
Name2 = "Looooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong test",
Name3 = "Short test",
Name4 = "Nothing"
});
Entries = testEntries;
}
private ObservableCollection<TestViewModel> _entries;
public ObservableCollection<TestViewModel> Entries
{
get { return _entries; }
set
{
_entries = value;
OnPropertyChanged();
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
}
Second viewmodel
using System.ComponentModel;
using System.Runtime.CompilerServices;
namespace WPFSO
{
public class TestViewModel : INotifyPropertyChanged
{
private string _name;
private string _name2;
private string _name3;
private string _name4;
public string Name
{
get { return _name; }
set
{
_name = value;
OnPropertyChanged();
}
}
public string Name2
{
get { return _name2; }
set
{
_name2 = value;
OnPropertyChanged();
}
}
public string Name3
{
get { return _name3; }
set
{
_name3 = value;
OnPropertyChanged();
}
}
public string Name4
{
get { return _name4; }
set
{
_name4 = value;
OnPropertyChanged();
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
}
Set
TextTrimming="CharacterEllipsis"
on the TextBlock.
It works for me. As you have defined the middle column should be twice the size of the other.
I find myself in a similar situation but TextTrimming isn't available.
Ends up binding child Width to Grid.ActualWidth with a Converter converts the ratio into absolute width.
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="50*" />
<ColumnDefinition Width="100*" />
<ColumnDefinition Width="50*" />
</Grid.ColumnDefinitions>
<Grid.Resources>
<local:PartConv x:Key="partConv"/>
<sys:Double x:Key="r0">0.25</sys:Double>
<sys:Double x:Key="r1">0.5</sys:Double>
<sys:Double x:Key="r2">0.25</sys:Double>
</Grid.Resources>
<TextBlock Text="Foo" Grid.Column="0"
Width="{Binding ActualWidth,
RelativeSource={RelativeSource AncestorType=Grid}},
Converter={StaticResource partConv},
ConverterParameter={StaticResource r0}}"/>
<TextBlock Text="Some long text here which overflows" Grid.Column="1"
Width="{Binding ActualWidth,
RelativeSource={RelativeSource AncestorType=Grid}},
Converter={StaticResource partConv},
ConverterParameter={StaticResource r1}}"/>
<TextBlock Text="Foo" Grid.Column="2"
Width="{Binding ActualWidth,
RelativeSource={RelativeSource AncestorType=Grid}},
Converter={StaticResource partConv},
ConverterParameter={StaticResource r2}}"/>
</Grid>
[ValueConversion(typeof(double), typeof(double))]
public class PartConv : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
=> ((double)value) * ((double)parameter);
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
=> ((double)value) / ((double)parameter);
}
I have a TabControl. Each tab Item i.e newly added Tab is rendered using content template selector. But each time i switch between the tabs, content template selector is getting called.
I wanted to stop this. This is because, In the in Tabcontrol, User have provided actions to change the layout, since in the selection change event of the tab Item content template is getting called, it is getting difficult for me to retain the layout what user has changed during Tab Item Selection change Event.
Following is code i am using for the TabControl
<TabControl Grid.Column="2" x:Name="MainTabControl" HorizontalAlignment="Stretch" Margin="0,12,0,7"
SelectedItem="{Binding SelectedTabItem}"
Visibility="{Binding TabsVisible}"
ItemsSource="{Binding ToolsList,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}"
ItemTemplate="{StaticResource ToolDisplayDataTemplate}"
commands:FrameworkUICommandList.TabItemChangedCommand="{Binding Path=TabItemChangedCommand}"
>
<TabControl.ContentTemplate>
<DataTemplate>
<ContentControl Content="{Binding}" ContentTemplateSelector="{DynamicResource toolTabDataItemTemplateSelector}"/>
</DataTemplate>
</TabControl.ContentTemplate>
<TabControl.ItemContainerStyle>
<Style TargetType="TabItem" BasedOn="{StaticResource TabItemStyle}">
<Setter Property="AutomationProperties.AutomationId" Value="{Binding ToolID}" />
<Setter Property="ToolTip" Value="{Binding ToolID,Converter={StaticResource ResourceKey=tabItemTooltipConverter}}"/>
</Style>
</TabControl.ItemContainerStyle>
</TabControl>
UPDATE : I have created a sample project to explain my problem. Could not share the project anywhere, so updating the code snippet in main question. sorry for that.
In the sample project, user can add TabItem dynamically to TabControl. Eash Tabcontent can show two Grid panel and they are separated by Grid Splitter. Display of second grid is Based on some flag (in this example ShowSecondPanel). If user click on "Show / Hide Second Panel" button, then second panel content will be shown for the current selected tab.
The Problem is, user can re-size the panels using Grid Splitter, but when user navigates to some other tab and comes back to previous one, the grid splitter position changes to original position.
Hope I am clear on describing the problem.
Code for MainWindow.xaml
<Window x:Class="TabControlTestApp.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:TabControlTestApp"
Title="MainWindow" Height="500" Width="700">
<Window.DataContext>
<local:MainWindowViewModel />
</Window.DataContext>
<Window.Resources>
<local:TabContentTemplateSelector x:Key="tabContentTemplateSelector" />
<DataTemplate x:Key="TabitemDataTemplate">
<StackPanel Width="50" Height="50">
<TextBlock Text="{Binding TabFirstPanel.Name}"></TextBlock>
</StackPanel>
</DataTemplate>
<DataTemplate x:Key="TabContentWithFirstPanel">
<Grid>
<Grid.RowDefinitions>
<RowDefinition></RowDefinition>
<RowDefinition></RowDefinition>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition></ColumnDefinition>
<ColumnDefinition></ColumnDefinition>
</Grid.ColumnDefinitions>
<TextBlock Grid.Row="0" Grid.Column="0">Name :</TextBlock>
<TextBlock Grid.Row="0" Grid.Column="1" Text="{Binding TabFirstPanel.Name}"></TextBlock>
</Grid>
</DataTemplate>
<DataTemplate x:Key="TabContentWithBothPanel">
<Grid>
<Grid.RowDefinitions>
<RowDefinition></RowDefinition>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="200"></ColumnDefinition>
<ColumnDefinition Width="5"></ColumnDefinition>
<ColumnDefinition Width="Auto"></ColumnDefinition>
</Grid.ColumnDefinitions>
<Grid Grid.Row="0" Grid.Column="0">
<Grid.RowDefinitions>
<RowDefinition></RowDefinition>
<RowDefinition></RowDefinition>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition></ColumnDefinition>
<ColumnDefinition></ColumnDefinition>
</Grid.ColumnDefinitions>
<TextBlock Grid.Row="0" Grid.Column="0">Name :</TextBlock>
<TextBlock Grid.Row="0" Grid.Column="1" Text="{Binding TabFirstPanel.Name}"></TextBlock>
</Grid>
<GridSplitter Grid.Row="0" Grid.Column="1"
VerticalAlignment="Stretch"
Width="5"
Height="Auto"
ResizeDirection="Columns"
ResizeBehavior="PreviousAndNext"
></GridSplitter>
<Grid Grid.Row="0" Grid.Column="2">
<Grid.RowDefinitions>
<RowDefinition></RowDefinition>
<RowDefinition></RowDefinition>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition></ColumnDefinition>
<ColumnDefinition></ColumnDefinition>
</Grid.ColumnDefinitions>
<TextBlock Grid.Row="0" Grid.Column="0">Additional Detail :</TextBlock>
<TextBlock Grid.Row="0" Grid.Column="1" Text="{Binding TabSecondPanel.AdditionalDetails}"></TextBlock>
</Grid>
</Grid>
</DataTemplate>
</Window.Resources>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="50"></RowDefinition>
<RowDefinition></RowDefinition>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="150"></ColumnDefinition>
<ColumnDefinition></ColumnDefinition>
</Grid.ColumnDefinitions>
<StackPanel Grid.Row="1" Grid.Column="0">
<Button Content="Load Tab : 1" Name="LoadTab1" Width="100" Height="50" Command="{Binding LoadTabCommand}" CommandParameter="{Binding ElementName=LoadTab1}"></Button>
<Button Content="Load Tab : 2" Name="LoadTab2" Width="100" Height="50" Command="{Binding LoadTabCommand}" CommandParameter="{Binding ElementName=LoadTab2}"></Button>
<Button Content="Load Tab : 3" Name="LoadTab3" Width="100" Height="50" Command="{Binding LoadTabCommand}" CommandParameter="{Binding ElementName=LoadTab3}"></Button>
</StackPanel>
<Button Content="Close All tab" Width="100" Height="40" x:Name="CloseAllTab"></Button>
<Button Content="Show / Hide Second Panel" x:Name="ShowHideSecondPanelInTab"
Grid.Row="0" Grid.Column="1" Width="150"
Command="{Binding LoadTabCommand}" CommandParameter="{Binding ElementName=ShowHideSecondPanelInTab}"></Button>
<TabControl Grid.Row="1" Grid.Column="1"
ItemsSource="{Binding TabContentList,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}"
SelectedItem="{Binding SelectedTabItem}"
ItemTemplate="{StaticResource ResourceKey=TabitemDataTemplate}"
ContentTemplateSelector="{StaticResource ResourceKey=tabContentTemplateSelector}"
>
</TabControl>
</Grid>
</Window>
Code For MainWindowViewModel
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Input;
using System.ComponentModel;
using System.Collections.ObjectModel;
namespace TabControlTestApp
{
class MainWindowViewModel : INotifyPropertyChanged
{
public MainWindowViewModel()
{
this.loadTabCommand = new LoadTabCommand(this);
tabContentList = new ObservableCollection<TabContent>();
}
public ICommand LoadTabCommand
{
get
{
return this.loadTabCommand;
}
set
{
this.loadTabCommand = value;
}
}
public ObservableCollection<TabContent> TabContentList
{
get
{
return tabContentList;
}
set
{
tabContentList = value;
OnPropertyChanged("TabContentList");
}
}
public TabContent SelectedTabItem
{
get
{
return selectedTabItem;
}
set
{
selectedTabItem = value;
OnPropertyChanged("SelectedTabItem");
}
}
public void LoadFirstTab()
{
TabFirstPanel firstPanel = new TabFirstPanel() { Name = "John-1", Age = "31" };
TabSecondPanel secondPanel = new TabSecondPanel() { AdditionalDetails="Some Details for First Tab" };
TabContent firstTabContent = new TabContent() { TabFirstPanel = firstPanel, TabSecondPanel = secondPanel, ShowSecondPanel = false };
tabContentList.Add(firstTabContent);
SelectedTabItem = firstTabContent;
}
public void LoadSecondTab()
{
TabFirstPanel firstPanel = new TabFirstPanel() { Name = "John-2", Age = "31" };
TabSecondPanel secondPanel = new TabSecondPanel() { AdditionalDetails = "Some Details for second Tab" };
TabContent secondTabContent= new TabContent() { TabFirstPanel = firstPanel, TabSecondPanel = secondPanel, ShowSecondPanel=false };
tabContentList.Add(secondTabContent);
SelectedTabItem = secondTabContent;
}
public void LoadThirdTab()
{
TabFirstPanel firstPanel = new TabFirstPanel() { Name = "John-3", Age = "31" };
TabSecondPanel secondPanel = new TabSecondPanel() { AdditionalDetails = "Some Details for Third Tab" };
TabContent ThirdTabContent = new TabContent() { TabFirstPanel = firstPanel, TabSecondPanel = secondPanel, ShowSecondPanel = false };
tabContentList.Add(ThirdTabContent);
SelectedTabItem = ThirdTabContent;
}
public void ShowHideSecondPanelInTab()
{
TabContent currentTabContent = SelectedTabItem;
int currentIndex = tabContentList.IndexOf(SelectedTabItem);
if (currentTabContent.ShowSecondPanel)
{
currentTabContent.ShowSecondPanel = false;
}
else
{
currentTabContent.ShowSecondPanel = true;
}
TabContentList.RemoveAt(currentIndex);
TabContentList.Insert(currentIndex, currentTabContent);
OnPropertyChanged("TabContentList");
SelectedTabItem = currentTabContent;
}
private TabContent selectedTabItem = null;
private ObservableCollection<TabContent> tabContentList = null;
private ICommand loadTabCommand = null;
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
}
Code for TabContentTemplateSelector.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Controls;
using System.Windows;
namespace TabControlTestApp
{
class TabContentTemplateSelector : DataTemplateSelector
{
public override System.Windows.DataTemplate SelectTemplate(object item, System.Windows.DependencyObject container)
{
FrameworkElement element = container as FrameworkElement;
if (element != null && item != null)
{
TabContent tabContent = (TabContent)item;
if (tabContent.ShowSecondPanel)
{
return element.FindResource("TabContentWithBothPanel") as DataTemplate;
}
else
{
return element.FindResource("TabContentWithFirstPanel") as DataTemplate;
}
}
else
{
return base.SelectTemplate(item, container);
}
}
}
}
Code for TabContent Data Object
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace TabControlTestApp
{
class TabFirstPanel
{
public string Name { get; set; }
public string Age { get; set; }
}
class TabSecondPanel
{
public string AdditionalDetails { get; set; }
}
class TabContent
{
public TabFirstPanel TabFirstPanel { get; set; }
public TabSecondPanel TabSecondPanel { get; set; }
public bool ShowSecondPanel { get; set; }
}
}
Code for LoadTabCommand Class
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Input;
namespace TabControlTestApp
{
class LoadTabCommand : ICommand
{
MainWindowViewModel mainWindowViewModel = null;
public LoadTabCommand(MainWindowViewModel mainWindowViewModel)
{
this.mainWindowViewModel = mainWindowViewModel;
}
public bool CanExecute(object parameter)
{
return true;
}
public event EventHandler CanExecuteChanged;
public void Execute(object parameter)
{
System.Windows.Controls.Button btn = (System.Windows.Controls.Button)parameter;
switch (btn.Name)
{
case "LoadTab1":
mainWindowViewModel.LoadFirstTab();
break;
case "LoadTab2":
mainWindowViewModel.LoadSecondTab();
break;
case "LoadTab3":
mainWindowViewModel.LoadThirdTab();
break;
case "ShowHideSecondPanelInTab":
mainWindowViewModel.ShowHideSecondPanelInTab();
break;
}
}
}
}
I took a look at your example. Thanks for posting code. Now I understand your issue completely. Before I suggested to change DynamicResource to StaticResource I thought you wanted to only look up once for your DataTemplate.
Now I see you wish to keep the instance of DataTemplate alive so TabControl doesn't destroy it when changing tabs.
Here is the solution:
<Window.DataContext>
<local:MainWindowViewModel />
</Window.DataContext>
<Window.Resources>
<local:TabContentTemplateSelector x:Key="tabContentTemplateSelector" />
<DataTemplate x:Key="TabitemDataTemplate">
<StackPanel Width="50" Height="50">
<TextBlock Text="{Binding TabFirstPanel.Name}"></TextBlock>
</StackPanel>
</DataTemplate>
<Grid x:Key="template1">
<Grid.RowDefinitions>
<RowDefinition></RowDefinition>
<RowDefinition></RowDefinition>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition></ColumnDefinition>
<ColumnDefinition></ColumnDefinition>
</Grid.ColumnDefinitions>
<TextBlock Grid.Row="0" Grid.Column="0">Name :</TextBlock>
<TextBlock Grid.Row="0" Grid.Column="1" Text="{Binding TabFirstPanel.Name}"></TextBlock>
</Grid>
<Grid x:Key="template2">
<Grid.RowDefinitions>
<RowDefinition></RowDefinition>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="200"></ColumnDefinition>
<ColumnDefinition Width="5"></ColumnDefinition>
<ColumnDefinition Width="Auto"></ColumnDefinition>
</Grid.ColumnDefinitions>
<Grid Grid.Row="0" Grid.Column="0">
<Grid.RowDefinitions>
<RowDefinition></RowDefinition>
<RowDefinition></RowDefinition>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition></ColumnDefinition>
<ColumnDefinition></ColumnDefinition>
</Grid.ColumnDefinitions>
<TextBlock Grid.Row="0" Grid.Column="0">Name :</TextBlock>
<TextBlock Grid.Row="0" Grid.Column="1" Text="{Binding TabFirstPanel.Name}"></TextBlock>
</Grid>
<GridSplitter Grid.Row="0" Grid.Column="1"
VerticalAlignment="Stretch"
Width="5"
Height="Auto"
ResizeDirection="Columns"
ResizeBehavior="PreviousAndNext"/>
<Grid Grid.Row="0" Grid.Column="2">
<Grid.RowDefinitions>
<RowDefinition></RowDefinition>
<RowDefinition></RowDefinition>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition></ColumnDefinition>
<ColumnDefinition></ColumnDefinition>
</Grid.ColumnDefinitions>
<TextBlock Grid.Row="0" Grid.Column="0">Additional Detail :</TextBlock>
<TextBlock Grid.Row="0" Grid.Column="1" Text="{Binding TabSecondPanel.AdditionalDetails}"></TextBlock>
</Grid>
</Grid>
<DataTemplate x:Key="TabContentWithFirstPanel">
<ContentPresenter Content="{StaticResource template1}"/>
</DataTemplate>
<DataTemplate x:Key="TabContentWithBothPanel">
<ContentPresenter Content="{StaticResource template2}"/>
</DataTemplate>
If you just copy past that you will do fine.
By the way, just as sidenote for you, destroying and building up DataTemplates is very important in wpf for releasing unmanaged memory.
I don't believe you may avoid this behavior, unless you use another control other than the TabControl.
The TabControl is a ItemControl-derived component: if its content is created via DataTemplate (as you done), the actual content is generated every time upon the current selection.
Another option is to fill the TabControl with direct content, and the inner controls should be preserved across the selection.
Have a look here:
http://msdn.microsoft.com/en-us/library/system.windows.controls.tabcontrol(v=vs.110).aspx
UPDATE: First off, I am not sure to have understand what you want, but here is a solution based on what I mean.
The fundamental trick is hosting directly the tab-contents, instead of creating via data-templating. That is:
<TabControl>
<TabItem Header="Tab1">
<TextBlock
Text="Page 1"
HorizontalAlignment="Center"
VerticalAlignment="Center"
/>
</TabItem>
<TabItem Header="Tab2">
<CheckBox
Content="check me"
HorizontalAlignment="Center"
VerticalAlignment="Center"
/>
</TabItem>
<TabItem Header="Tab3">
<TextBlock
Text="Page 3"
HorizontalAlignment="Center"
VerticalAlignment="Center"
/>
</TabItem>
</TabControl>
The above snippet should "persist" the checkbox value across the tabs flipping.
But you need (or desire) some kind of templating, or a dynamic way to create the right content upon a certain data added to the tabcontrol.
The following trick should solve your problem:
<TabControl>
<TabItem
Header="Tab1"
>
<ContentControl
Content="{Binding Path=A}"
ContentTemplateSelector="{StaticResource sel}"
/>
</TabItem>
<TabItem
Header="Tab2"
>
<ContentControl
Content="{Binding Path=B}"
ContentTemplateSelector="{StaticResource sel}"
/>
</TabItem>
<TabItem
Header="Tab3"
>
<ContentControl
Content="{Binding Path=C}"
ContentTemplateSelector="{StaticResource sel}"
/>
</TabItem>
</TabControl>
This is using a trivial set of templates as follows:
<Window.Resources>
<local:MySelector x:Key="sel" />
<DataTemplate x:Key="dtplA">
<StackPanel Margin="30,30">
<TextBlock Text="A: " />
<TextBox />
</StackPanel>
</DataTemplate>
<DataTemplate x:Key="dtplB">
<StackPanel Margin="30,30">
<TextBlock Text="B: " />
<TextBox />
</StackPanel>
</DataTemplate>
<DataTemplate x:Key="dtplC">
<StackPanel Margin="30,30">
<TextBlock Text="C: " />
<TextBox />
</StackPanel>
</DataTemplate>
</Window.Resources>
And the behind-code is even more trivial:
public class VM
{
public A A { get; set; }
public B B { get; set; }
public C C { get; set; }
}
public class A { }
public class B { }
public class C { }
public class MySelector : DataTemplateSelector
{
public override DataTemplate SelectTemplate(object item, DependencyObject container)
{
if (item != null)
{
return (DataTemplate)((FrameworkElement)container).FindResource("dtpl" + item.GetType().Name);
}
else
{
return base.SelectTemplate(item, container);
}
}
}
If you try this small example, the text typed into the textboxes will persist across the tabs flipping. That is, the templates will be called once only.
Let me know.
When I run the following Silverlight app, it gives me the error:
AG_E_PARSER_BAD_PROPERTY_VALUE [Line:
12 Position: 35]
I've tried the same code in WPF and it runs fine, i.e. the middle grid row correctly resizes based on the bound value.
What do I have to change in this code to avoid this error in Silverlight?
XAML:
<UserControl x:Class="TestRowHeight222.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:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d" d:DesignWidth="640" d:DesignHeight="480">
<Grid x:Name="LayoutRoot">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="30"/>
<RowDefinition Height="{Binding ContentHeight}"/>
<RowDefinition Height="30"/>
</Grid.RowDefinitions>
<StackPanel Grid.Row="0" Background="Tan">
<TextBlock Text="row0" />
</StackPanel>
<StackPanel Grid.Row="1" Background="Beige" Orientation="Horizontal">
<TextBlock Text="The height should be: "/>
<TextBlock Text="{Binding ContentHeight}"/>
</StackPanel>
<StackPanel Grid.Row="2" Background="Tan">
<TextBlock Text="row2"/>
</StackPanel>
</Grid>
</UserControl>
Code Behind:
using System.Windows.Controls;
using System.ComponentModel;
namespace TestRowHeight222
{
public partial class MainPage : UserControl, INotifyPropertyChanged
{
#region ViewModelProperty: ContentHeight
private int _contentHeight;
public int ContentHeight
{
get
{
return _contentHeight;
}
set
{
_contentHeight = value;
OnPropertyChanged("ContentHeight");
}
}
#endregion
public MainPage()
{
InitializeComponent();
DataContext = this;
ContentHeight = 50;
}
#region INotifiedProperty Block
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
#endregion
}
}
This is as close as I can get, I don't know if it's suitable to your situation.
<Grid x:Name="LayoutRoot">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="30"/>
<RowDefinition Height="Auto" />
<RowDefinition Height="30"/>
</Grid.RowDefinitions>
<StackPanel Grid.Row="0" Background="Tan">
<TextBlock Text="row0" />
</StackPanel>
<Grid Grid.Row="1" Height="{Binding ContentHeight}">
<StackPanel Background="Beige" Orientation="Horizontal">
<TextBlock Text="The height should be: "/>
<TextBlock Text="{Binding ContentHeight}"/>
</StackPanel>
</Grid>
<StackPanel Grid.Row="2" Background="Tan">
<TextBlock Text="row2"/>
</StackPanel>
</Grid>
Also change your ContentHeight property to a double.