I have the following, very simple repro case. It's a Universal Windows (Phone) 8.1 app. I have a very simple model:
public class SimpleModel
{
public bool IsLoading { get; set; } = false;
}
and a collection of these models, defined as shared classes:
public class SimpleModelCollection : List<SimpleModel>
{
public SimpleModelCollection()
{
this.Add(new SimpleModel());
this.Add(new SimpleModel());
this.Add(new SimpleModel());
this.Add(new SimpleModel());
this.Add(new SimpleModel());
this.Add(new SimpleModel());
}
}
I have only a single page in both Windows and Windows Phone 8.1 project. They have identical XAML, a simple ItemsControl:
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<ItemsControl x:Name="items" HorizontalAlignment="Center">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Grid>
<interactivity:Interaction.Behaviors>
<core:DataTriggerBehavior Binding="{Binding IsLoading}" Value="True">
<core:ChangePropertyAction TargetObject="{Binding ElementName=text}" PropertyName="Visibility" Value="Visible"/>
</core:DataTriggerBehavior>
<core:DataTriggerBehavior Binding="{Binding IsLoading}" Value="False">
<core:ChangePropertyAction TargetObject="{Binding ElementName=text}" PropertyName="Visibility" Value="Collapsed"/>
</core:DataTriggerBehavior>
</interactivity:Interaction.Behaviors>
<StackPanel>
<TextBlock x:Name="text" Text="Should I be visible?" FontSize="26"></TextBlock>
</StackPanel>
</Grid>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</Grid>
Of course, Behaviors SDK is added to both (Win & WP 8.1) projects.
In code-behind, I set the ItemsSource to an instance of the previously mentioned simple model collection. Windows Phone constructor:
public MainPage()
{
this.InitializeComponent();
this.items.ItemsSource = new SimpleModelCollection();
this.NavigationCacheMode = NavigationCacheMode.Required;
}
and Windows constructor:
public MainPage()
{
this.InitializeComponent();
this.items.ItemsSource = new SimpleModelCollection();
}
What would you expect as a result if IsLoading was initialized to false:
public bool IsLoading { get; set; } = false;
Let me show you what the app looks like if Isloading is initialized to false on Windows Phone:
This is OK, and completely expected, since the Visibility maps to the bool value, so the TextBlocks should be collapsed if IsLoading is false. But on Windows, they are not:
My question is - why? What am I missing?
This is also problematic when comparing WP 8.1 behavior with the behavior in Windows 10 UWP. In UWP it behaves just like on Windows 8.1, which make porting from WP 8.1 to UWP a bit painful.
EDIT: The full repro project is here: https://github.com/igrali/BehaviorsSDK_Bug
This is indeed a bug. I can see two ways to fix it.
First, if you absolutely know that the initial state of the TextBlocks should be Collapsed, you can default them to Collapsed in XAML as it only doesn't work for the first time.
Alternatively, you can attach all the Behaviors to the Textblock directly inside the template. This should work too.
<DataTemplate>
<Grid>
<StackPanel>
<TextBlock x:Name="text" Text="Should I be visible?" FontSize="26">
<interactivity:Interaction.Behaviors>
<core:DataTriggerBehavior Binding="{Binding IsLoading}" Value="True">
<core:ChangePropertyAction TargetObject="{Binding ElementName=text}" PropertyName="Visibility" Value="Visible" />
</core:DataTriggerBehavior>
<core:DataTriggerBehavior Binding="{Binding IsLoading}" Value="False">
<core:ChangePropertyAction TargetObject="{Binding ElementName=text}" PropertyName="Visibility" Value="Collapsed" />
</core:DataTriggerBehavior>
</interactivity:Interaction.Behaviors>
</TextBlock>
<Button Click="Button_Click" Content="Visible?" />
</StackPanel>
</Grid>
</DataTemplate>
Related
How do I get a datagrid like this with templates for rows as well as for repeatable cells?
DataGrid is bound to a property called Blocks, which is a BindingList of Block type, defined in the view model:
<DataGrid x:Name="dgBlocks" DockPanel.Dock="Left"
Margin="20,10,10,20" AutoGenerateColumns="False"
ItemsSource="{Binding Blocks, Mode=TwoWay}"
IsSynchronizedWithCurrentItem="True"
CanUserAddRows="False"
SelectionUnit="Cell" SelectionMode="Single"
EnableColumnVirtualization="False"
EnableRowVirtualization="False"
SelectedIndex="-1" MinRowHeight="10"
LoadingRow="dg_LoadingRow"
SelectionChanged="dgBlocks_SelectionChanged"
>
<DataGrid.Resources>
<helper:BindingProxy x:Key="proxy" Data="{Binding}" />
<!--todo: change to symbol’s ‘ReadOnly’ property instead of block’s ‘Permission’ -->
<Style x:Key="CellPermissionStyle" TargetType="DataGridCell">
<Setter Property="Background" Value="{Binding Permission, Converter={StaticResource BlockPermissionToBrushConverter}}" />
<Setter Property="IsEnabled" Value="{Binding Permission, Converter={StaticResource BlockPermissionToBoolConverter}}" />
</Style>
</DataGrid.Resources>
<DataGrid.RowHeaderTemplate>
<DataTemplate>
<TextBlock Name="txtBlockRowHeader"
Text="{Binding Path=Header,
RelativeSource={RelativeSource AncestorType=DataGridRow}}">
</TextBlock>
</DataTemplate>
</DataGrid.RowHeaderTemplate>
<DataGrid.Columns>
<DataGridTemplateColumn>
<!-- Selection checkboxes -->
<DataGridTemplateColumn.Header>
<StackPanel>
<CheckBox HorizontalAlignment="Center" IsThreeState="True" ToolTip="Select All" Margin="10,0,0,0">
<CheckBox.IsChecked>
<Binding Path="DataContext.SelectAll"
RelativeSource="{RelativeSource AncestorType={x:Type UserControl}}"
Mode="TwoWay"
UpdateSourceTrigger="PropertyChanged"
/>
</CheckBox.IsChecked>
</CheckBox>
</StackPanel>
</DataGridTemplateColumn.Header>
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<CheckBox Margin="5,0,0,0" IsChecked="{Binding IsSelected, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<DataGridTemplateColumn Width="50">
<DataGridTemplateColumn.Header>
<TextBlock Text="Repeat" HorizontalAlignment="Center" />
</DataGridTemplateColumn.Header>
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<telerik:RadNumericUpDown Name="nudRepeatBlock" ValueFormat="Numeric" Width="40"
ToolTip="Repeat block number of times" IsInteger="True" IsEditable="True"
Minimum="1" Maximum="100" UpdateValueEvent="PropertyChanged"
Value="{Binding Repeat, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" MinWidth="40" />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<DataGridTextColumn Header="Name" Binding="{Binding Name}" IsReadOnly="True" CanUserReorder="False" CanUserResize="False"
Visibility="{Binding Data.Encoding, Converter={StaticResource Encoding8b10bToCollapsedConverter}, Source={StaticResource proxy}}">
<DataGridTextColumn.HeaderStyle>
<Style TargetType="DataGridColumnHeader">
<Setter Property="HorizontalContentAlignment" Value="Center" />
</Style>
</DataGridTextColumn.HeaderStyle>
</DataGridTextColumn>
<!--todo: change… -->
<DataGridTextColumn Header="[0]" Binding="{Binding .Symbols[0].SymbolText, UpdateSourceTrigger=LostFocus}" CellStyle="{StaticResource CellPermissionStyle}" />
<DataGridTextColumn Header="[1]" Binding="{Binding .Symbols[1].SymbolText, UpdateSourceTrigger=LostFocus}" CellStyle="{StaticResource CellPermissionStyle}" />
<!-- … -->
<DataGridTextColumn Header="[15]" Binding="{Binding .Symbols[15].SymbolText, UpdateSourceTrigger=LostFocus}" CellStyle="{StaticResource CellPermissionStyle}" />
</DataGrid.Columns>
</DataGrid>
Each Block object has a property called Symbols, defined as a BindingList of Symbol type, and some other properties. Symbols get shown in columns with headers [0], 1, etc.
Other block’s properties shown in other columns. E.g. IsSelected for checkbox, Reapeat, Name.
public interface ISymbol
{
int Index { get; set; }
bool ReadOnly { get; set; }
string SymbolText { get; set; }
}
public class BlockBase : ObservableObject, IDataErrorInfo
{
public bool IsSelected
{
get { return _isSelected; }
set
{
if (value == _isSelected)
return;
_isSelected = value;
OnPropertyChanged("IsSelected");
RaiseSelectionChangedEvent();
}
}
public string Name { get; set; }
public virtual BindingList<ISymbol> Symbols { get; set; }
public BlockPermission Permission { get; set; }
public int Repeat { get; set; }
}
public class Symbol : ObservableObject, ISymbol
{
DisplayFormat Format { get; set; }
public int Index { get; set; }
public virtual bool ReadOnly { get; set; }
public virtual string SymbolText
{
get { return (Format == DisplayFormat.Binary) ? _binSymbol : _hexSymbol;
}
set
{
// … validate and set _binSymbol & _hexSymbol values
OnPropertyChanged("SymbolText");
}
}
Now, I need to bind each symbol’s cell 'IsEnabled' property to Symbol's 'ReadOnly' property.
I tried to define this in DataGrid.Resources as CellPermissionStyle, but I don’t know how to access a Symbol on this level.
Also, it would be nice to replace all repeating DataGridTextColumn defenitions for symbols with a template.
Would somebody help me?
My researches just confirmed that unfortunately there is no way to do this with templates or styles applicable to a subset of columns. It is also imposible to send a parameter to a style or template as Mishka suggested :(.
So, I've just ended up with 'copy-paste-modify' cell style for each of my 16 columns like that:
<DataGridTextColumn Header="[0]" Binding="{Binding .Symbols[0].SymbolText, UpdateSourceTrigger=LostFocus}">
<DataGridTextColumn.CellStyle>
<Style TargetType="DataGridCell">
<Setter Property="Background" Value="{Binding .Symbols[0].ReadOnly, Converter={StaticResource SymbolReadOnlyToBrushConverter}}" />
<Setter Property="IsEnabled" Value="{Binding .Symbols[0].ReadOnly, Converter={StaticResource SymbolReadOnlyToEnabledConverter}}" />
<Setter Property="TextBlock.TextAlignment" Value="Center" />
</Style>
</DataGridTextColumn.CellStyle>
</DataGridTextColumn>
...
<DataGridTextColumn Header="[15]" Binding="{Binding .Symbols[15].SymbolText, UpdateSourceTrigger=LostFocus}">
<DataGridTextColumn.CellStyle>
<Style TargetType="DataGridCell">
<Setter Property="Background" Value="{Binding .Symbols[15].ReadOnly, Converter={StaticResource SymbolReadOnlyToBrushConverter}}" />
<Setter Property="IsEnabled" Value="{Binding .Symbols[15].ReadOnly, Converter={StaticResource SymbolReadOnlyToEnabledConverter}}" />
<Setter Property="TextBlock.TextAlignment" Value="Center" />
</Style>
</DataGridTextColumn.CellStyle>
</DataGridTextColumn>
Yes, I know, this XAML looks ugly, but it works for me without a risky redesign of existing code base.
I'd be happy if someone could suggest a better, more elegant way doing this in XAML.
The request is that the row of my ListView blinks when the property SelectedItem of the ViewModel raises change.
This is my code, the problem is that it works only first time. Subsequent changes are ignored.
<DataTemplate x:Key="myDataTemplate">
<Grid x:Name="myGrid">
<Interactivity:Interaction.Behaviors>
<Core:DataTriggerBehavior Binding="{Binding SelectedItem}" Value="True">
<Media:ControlStoryboardAction>
<Media:ControlStoryboardAction.Storyboard>
<Storyboard>
<ColorAnimation
To="#009ABF"
Storyboard.TargetName="myGrid"
Storyboard.TargetProperty="(Grid.Background).(SolidColorBrush.Color)"
AutoReverse="True"
Duration="0:0:1"
RepeatBehavior="1x" />
</Storyboard>
</Media:ControlStoryboardAction.Storyboard>
</Media:ControlStoryboardAction>
</Core:DataTriggerBehavior>
</Interactivity:Interaction.Behaviors>
<TextBlock Text="{Binding Name}"
Grid.Column="1"
VerticalAlignment="Top"
HorizontalAlignment="Left"
Margin="0,2,10,0"
FontSize="16"
TextAlignment="Left"/>
<!--OMISSIS-->
</Grid>
SelectedItem code :
public bool SelectedItem
{
get
{
return this.selectedItem;
}
set
{
this.selectedItem = value;
this.RaisePropertyChanged();
}
}
This is the solution i found.
1) Use Completed event of the Storyboard
<Storyboard Completed="SelectedItemReset" FillBehavior="Stop">
3) Use the GalaSoft.MvvmLight.Messaging.Messenger to comunicate from CodeBehind and ViewModel the reset of the property SelectedItem
Xaml
<ListView>
<ListView.ItemTemplate>
<DataTemplate>
<Grid x:Name="DataTemplateGrid">
<Interactivity:Interaction.Behaviors>
<Core:DataTriggerBehavior Binding="{Binding SelectedItem}" ComparisonCondition="Equal" Value="True">
<Media:ControlStoryboardAction ControlStoryboardOption="Play">
<Media:ControlStoryboardAction.Storyboard>
<Storyboard Completed="SelectedItemReset" FillBehavior="Stop">
<ColorAnimation
To="Lime"
Storyboard.TargetName="DataTemplateGrid"
Storyboard.TargetProperty="(Grid.Background).(SolidColorBrush.Color)"
Duration="0:0:1"/>
</Storyboard>
</Media:ControlStoryboardAction.Storyboard>
</Media:ControlStoryboardAction>
</Core:DataTriggerBehavior>
</Interactivity:Interaction.Behaviors>
<!--OMISSIS-->
</Grid>
</DataTemplate>
</ListView.ItemTemplate>
CodeBehind
private void SelectedItemReset(object sender, object e)
{
GalaSoft.MvvmLight.Messaging.Messenger.Default.Send<Mvvm.ViewModels.Units.SelectedItemResetMessage>(new Mvvm.ViewModels.Units.SelectedItemResetMessage());
}
MVVM Class .ctor
public MyViewModel()
{
GalaSoft.MvvmLight.Messaging.Messenger.Default.Register<SelectedItemResetMessage>(this, message =>
{
if (this.SelectedItem == true)
this.SelectedItem = false;
});
}
Note: My DataTemplate was in a separate file and linked to the ListView with the ItemTemplate property, this prevented me from calling the method Completed in CodeBehind.
I'm trying to close a flyout on a listview item click. The problem is that during runtime, the CallMethodAction can't find the hide method of the flyout menu. How can I fix this?
<Flyout x:Name="UnitFlyout">
<ListView x:Name="ArmyUnitListView" ItemsSource="{Binding Source={StaticResource ArmyUnitCollection}}" SelectionMode="Single" >
<ListView.GroupStyle>
<GroupStyle HidesIfEmpty="True" HeaderTemplate="{StaticResource ArmyListDataGroupTemplate}" />
</ListView.GroupStyle>
<ListView.ItemContainerStyle>
<Style TargetType="ListViewItem">
<Setter Property="HorizontalContentAlignment" Value="Stretch" />
</Style>
</ListView.ItemContainerStyle>
<interactivity:Interaction.Behaviors>
<core:EventTriggerBehavior EventName="SelectionChanged">
<core:InvokeCommandAction Command="{Binding AddUnitCommand}" CommandParameter="{Binding SelectedItem, ElementName=ArmyUnitListView}" />
<core:CallMethodAction TargetObject="UnitFlyout" MethodName="Hide"/>
</core:EventTriggerBehavior>
</interactivity:Interaction.Behaviors>
</ListView>
</Flyout>
I made a demo and reproduced the problem.The reason lies on the ElementName binding to popups.
ElementName bindings do not work within Flyout’s and other popups
Please see this Case.
I found this blog, which offers a workaround to fix this problem. And I've tried it with a demo,which works fine.
In your case, you can copy the FlyoutHelpers (in blog) class to your project; And add IsFlyoutOpen and SendCommand to your ViewModel like below:
public class MainPageViewModel : ViewModelBase
{
public RelayCommand SendCommand { get; set; }// bind this to your xaml
public List<String> MyData { get; set; }
private bool isFlyoutOpen;
public bool IsFlyoutOpen// bind this to your xaml
{
get { return isFlyoutOpen; }
set { this.Set(() => IsFlyoutOpen, ref isFlyoutOpen, value); }
}
public MainPageViewModel()
{
SendCommand = new RelayCommand(() =>
{
// Doing processing...
IsFlyoutOpen = false;
});
MyData = new List<string> { "winffee", "123", "this Data" };//this is sample data
}
}
And Bind the commands and properties to your xaml:
<Flyout x:Name="UnitFlyout"
local:FlyoutHelpers.Parent="{Binding ElementName=myBtn}"
local:FlyoutHelpers.IsOpen="{Binding IsFlyoutOpen,Mode=TwoWay}">
<ListView x:Name="ArmyUnitListView" SelectionMode="Single" ItemsSource="{Binding MyData}">
<ListView.ItemContainerStyle>
<Style TargetType="ListViewItem">
<Setter Property="HorizontalContentAlignment" Value="Stretch" />
</Style>
</ListView.ItemContainerStyle>
<interactivity:Interaction.Behaviors>
<core:EventTriggerBehavior EventName="SelectionChanged" SourceObject="{Binding ElementName=ArmyUnitListView}">
<!--<core:InvokeCommandAction Command="{Binding AddUnitCommand}" CommandParameter="{Binding SelectedItem, ElementName=ArmyUnitListView}" />-->
<core:InvokeCommandAction Command="{Binding SendCommand}" />
</core:EventTriggerBehavior>
</interactivity:Interaction.Behaviors>
</ListView>
</Flyout>
Here is my entire Demo: FlyoutSample
I'm building a Windows Store app with C#/XAML.
I have a simple ListView bound to an ItemsSource. There's a DataTemplate which defines the structure of each item and that has a ContentControl and a TextBlock in it.
I wish to change the Foreground colour of the TextBlock when the item is selected. Does anyone know how I can do this?
<ListView Grid.Column="1"
ItemsSource="{Binding Categories}"
ItemContainerStyle="{StaticResource CategoryListViewItemStyle}"
Background="{StaticResource DeepRedBrush}">
<ListView.ItemTemplate>
<DataTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<ContentControl Content="{Binding Id, Converter={StaticResource Cat2Icon}}" HorizontalAlignment="Left" VerticalAlignment="Center" Width="110" Foreground="#FF29BCD6"/>
<TextBlock x:Name="catName" HorizontalAlignment="Left" Margin="0" TextWrapping="Wrap" Text="{Binding Name}" Grid.Column="1" VerticalAlignment="Center" FontSize="18.667"
Foreground="White"/>
</Grid>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
At the moment it's set to "White", so all I need is some binding expression that will change the Foreground property depending on the selected state of the item in the listview.
This does what you are asking for.
Using this XAML
<Grid x:Name="LayoutRoot" Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<ListView x:Name="MyListView" ItemsSource="{Binding Items}" SelectionMode="Single" SelectedItem="{Binding Selected, Mode=TwoWay}">
<ListView.ItemTemplate>
<DataTemplate>
<Grid Height="100" Width="300">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="100" />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<Ellipse x:Name="ellipse">
<Ellipse.Fill>
<SolidColorBrush Color="{Binding Color}" />
</Ellipse.Fill>
</Ellipse>
<TextBlock Grid.Column="1" VerticalAlignment="Center" Margin="10" Text="{Binding Title}" Style="{StaticResource HeaderTextBlockStyle}" />
</Grid>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</Grid>
And this code behind:
public class MyModel : BindableBase
{
string _Title = default(string);
public string Title { get { return _Title; } set { SetProperty(ref _Title, value); } }
Color _Color = Colors.White;
public Color Color { get { return _Color; } set { SetProperty(ref _Color, value); } }
}
public class MyViewModel : BindableBase
{
public MyViewModel()
{
var items = Enumerable.Range(1, 10)
.Select(x => new MyModel { Title = "Title " + x.ToString() });
foreach (var item in items)
this.Items.Add(item);
}
MyModel _Selected = default(MyModel);
public MyModel Selected
{
get { return _Selected; }
set
{
if (this.Selected != null)
this.Selected.Color = Colors.White;
SetProperty(ref _Selected, value);
value.Color = Colors.Red;
}
}
ObservableCollection<MyModel> _Items = new ObservableCollection<MyModel>();
public ObservableCollection<MyModel> Items { get { return _Items; } }
}
public abstract class BindableBase : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected void SetProperty<T>(ref T storage, T value, [System.Runtime.CompilerServices.CallerMemberName] String propertyName = null)
{
if (!object.Equals(storage, value))
{
storage = value;
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
protected void RaisePropertyChanged([System.Runtime.CompilerServices.CallerMemberName] String propertyName = null)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
It will update your data template for you.
I want to make this quick point: updating the content of your list through the ViewModel is the easiest and most light-weight approach. In this case, I am updating the color which is bound to the ellipse. However, if this were a complex set of changes, I might just set a style instead. Another option is to hide and show an entire set of controls in the template. You cannot, however, change the data template because it will not be re-rendered until the grid re-draws, and that's not what you want to do.
Just like changing the Ellipse color, you could change the TextBlock Foreground like you asked in your question. Either way, this gets you what you want in the most elegant way.
Best of luck!
You can simply handle the SelectionChanged event on the ListView and change the Foreground of the previously selected item and the newly selected item by either changing a view model value on SelectedItem that is bound to your Foreground.
You can also find the TextBlock using ListView.ItemContainerGenerator.ContainerFromItem(ListView.SelectedItem) + VisualTreeHelper as in Jerry Nixon's blog post and changing the Foreground directly, though that technique has a problem if your ListView is virtualized (which it is by default), since if you scroll away from the selected item and back - the item view with the changed Foreground might be recycled and used for another item in your collection.
Another option is to bind the Foreground to the IsSelected property of the parent ListViewItem which you can do in many ways as well. You could for example put your entire DataTemplate in a UserControl and bind the Foreground to the Parent of that control. The problem is I think Parent is not a dependency property and I see no ParentChanged event on FrameworkElement (base class for UserControl that defines the Parent property), so it might be tough to go this route. Another way to bind these is to define an attached dependency property or behavior that would set up that binding for you, but that is complicated (though I have already created one you could use here).
Finally you could modify your ListView.ItemContainerStyle and change the SelectedBackground value. If that works - it would be the ideal solution.
<Style TargetType="ListViewItem">
<Setter Property="FontFamily" Value="{ThemeResource ContentControlThemeFontFamily}"/>
<Setter Property="FontSize" Value="{ThemeResource ControlContentThemeFontSize}"/>
<Setter Property="Background" Value="Transparent"/>
<Setter Property="TabNavigation" Value="Local"/>
<Setter Property="IsHoldingEnabled" Value="True"/>
<Setter Property="Margin" Value="0,0,18,2"/>
<Setter Property="HorizontalContentAlignment" Value="Left"/>
<Setter Property="VerticalContentAlignment" Value="Top"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ListViewItem">
<ListViewItemPresenter CheckHintBrush="{ThemeResource ListViewItemCheckHintThemeBrush}" CheckBrush="{ThemeResource ListViewItemCheckThemeBrush}" ContentMargin="4" ContentTransitions="{TemplateBinding ContentTransitions}" CheckSelectingBrush="{ThemeResource ListViewItemCheckSelectingThemeBrush}" DragForeground="{ThemeResource ListViewItemDragForegroundThemeBrush}" DragOpacity="{ThemeResource ListViewItemDragThemeOpacity}" DragBackground="{ThemeResource ListViewItemDragBackgroundThemeBrush}" DisabledOpacity="{ThemeResource ListViewItemDisabledThemeOpacity}" FocusBorderBrush="{ThemeResource ListViewItemFocusBorderThemeBrush}" HorizontalContentAlignment="{TemplateBinding HorizontalContentAlignment}" Padding="{TemplateBinding Padding}" PointerOverBackgroundMargin="1" PlaceholderBackground="{ThemeResource ListViewItemPlaceholderBackgroundThemeBrush}" PointerOverBackground="{ThemeResource ListViewItemPointerOverBackgroundThemeBrush}" ReorderHintOffset="{ThemeResource ListViewItemReorderHintThemeOffset}" SelectedPointerOverBorderBrush="{ThemeResource ListViewItemSelectedPointerOverBorderThemeBrush}" SelectionCheckMarkVisualEnabled="True" SelectedForeground="{ThemeResource ListViewItemSelectedForegroundThemeBrush}" SelectedPointerOverBackground="{ThemeResource ListViewItemSelectedPointerOverBackgroundThemeBrush}" SelectedBorderThickness="{ThemeResource ListViewItemCompactSelectedBorderThemeThickness}" SelectedBackground="{ThemeResource ListViewItemSelectedBackgroundThemeBrush}" VerticalContentAlignment="{TemplateBinding VerticalContentAlignment}"/>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
I've been struggeling with this a few days now and can't get it to work.
Maybe I'm not as good XAML programmer that I hoped I would be :)
Anyhow, my problem is that i want to bind a number of elements to a GridView and make them appear as squares without setting any width and height. The reason for this is that I want my GridView items to grow/shrink and expand to maximum size as the resolution or screen size vary.
Here is my XAML:
<UserControl.Resources>
<Style x:Key="MyItemContainerStyle" TargetType="ListViewItem">
<Setter Property="FontFamily" Value="Segoe UI" />
<Setter Property="FontWeight" Value="Bold" />
<Setter Property="HorizontalContentAlignment" Value="Stretch" />
<Setter Property="VerticalContentAlignment" Value="Stretch" />
<!--<Setter Property="Height" Value="{Binding RelativeSource={RelativeSource Self}, Path=Width}" />-->
</Style>
<DataTemplate x:Key="MyItemTemplate">
<Border CornerRadius="4"
BorderBrush="Black"
BorderThickness="1"
Background="Blue">
<TextBlock VerticalAlignment="Center" HorizontalAlignment="Center">X</TextBlock>
</Border>
</DataTemplate>
<ItemsPanelTemplate x:Key="MyItemsPanelTemplate">
<StackPanel Orientation="Horizontal" VerticalAlignment="Center" HorizontalAlignment="Center" />
</ItemsPanelTemplate>
</UserControl.Resources>
<Grid Background="White">
<GridView x:Name="MyGrid"
UseLayoutRounding="True"
ItemTemplate="{StaticResource MyItemTemplate}"
ItemsPanel="{StaticResource MyItemsPanelTemplate}"
ItemContainerStyle="{StaticResource MyItemContainerStyle}"
ScrollViewer.HorizontalScrollBarVisibility="Auto" ScrollViewer.VerticalScrollBarVisibility="Auto">
</GridView>
</Grid>
And this is my code-behind:
public sealed partial class BlankPage : Page
{
public BlankPage()
{
this.InitializeComponent();
MyGrid.Items.Add(new ListViewItem { Content = 1 });
MyGrid.Items.Add(new ListViewItem { Content = 2 });
MyGrid.Items.Add(new ListViewItem { Content = 3 });
}
/// <summary>
/// Invoked when this page is about to be displayed in a Frame.
/// </summary>
/// <param name="e">Event data that describes how this page was reached. The Parameter
/// property is typically used to configure the page.</param>
protected override void OnNavigatedTo(NavigationEventArgs e)
{
}
}
This however produces an output like this (rectangular items, not squares):
I would appreciate if someone who knows a bit more about XAML and WinRT (metro) development than I do, could explain this for me and maybe give me a working example.
Thanx!
EDIT
I got a tip to wrap my Border in a Viewbox as it seems to have some scaling/stretching abilities.
I played around a couple of hours but I can't really get it to work 100%.
This is my XAML now:
<UserControl.Resources>
<Style x:Key="MyItemContainerStyle" TargetType="ListViewItem">
<Setter Property="FontFamily" Value="Segoe UI" />
<Setter Property="FontWeight" Value="Bold" />
<Setter Property="HorizontalContentAlignment" Value="Stretch" />
<Setter Property="VerticalContentAlignment" Value="Stretch" />
</Style>
<DataTemplate x:Key="MyItemTemplate">
<Viewbox>
<Border CornerRadius="3"
BorderBrush="Black"
BorderThickness="1">
<Grid Background="Blue" MinHeight="50" MinWidth="50">
<TextBlock VerticalAlignment="Center" HorizontalAlignment="Center">X</TextBlock>
</Grid>
</Border>
</Viewbox>
</DataTemplate>
<ItemsPanelTemplate x:Key="MyItemsPanelTemplate">
<StackPanel Orientation="Horizontal" VerticalAlignment="Center" HorizontalAlignment="Center">
</StackPanel>
</ItemsPanelTemplate>
</UserControl.Resources>
<Grid Background="White">
<GridView x:Name="MyGrid"
UseLayoutRounding="True"
ItemTemplate="{StaticResource MyItemTemplate}"
ItemsPanel="{StaticResource MyItemsPanelTemplate}"
ItemContainerStyle="{StaticResource MyItemContainerStyle}"
ScrollViewer.HorizontalScrollBarVisibility="Disabled" ScrollViewer.VerticalScrollBarVisibility="Disabled">
</GridView>
</Grid>
This produces this output:
Now it seems to stretch itself to a sqaure, but it sretches itself outside the screen. I also ran this example in several resoultions and screen sizes and it shows the same output, which means it scales correctly.
Help would be appreciated.
Thanx!
Your commented out binding is close to something that could sort of work - only you would need to bind to ActualWidth:
<Setter Property="Height" Value="{Binding ActualWidth, RelativeSource={RelativeSource Self}}" />
Overall I would suggest using hardcoded values - they make things easier on the CPU to layout, easier to deterministically design and will get scaled by Windows when screen size and resolution will require that. If you want more control over that - you can bind both Width and Height to the same value from a view model that you change depending on the need or write a converter that will convert a hardcoded Width/Height value to actual value depending on detected screen size/resolution or settings.