I have a wpf grid with two columns that should be equally spaced. I know about star (*) sizing, but when I set the column width to be equally spaced, the actual column changes at runtime. The overall width of the grid will change based on the window size, but I want to force the two columns to always be of equal width inside that grid.
I've currently got a lot of nested elements assigned to the first column, and the second column only has an ItemsControl panel which only shows items when a button event action is taken... so there are times when it's empty. My assumption is that because the second column is empty, the first column is automatically taking up as much space as it can. Is there a way to force the columns to remain proportionally fixed? Here's my basic setup:
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="1*"></ColumnDefinition>
<ColumnDefinition Width="1*" ></ColumnDefinition>
</Grid.ColumnDefinitions>
<StackPanel Grid.Column="0">
<!-- Lots of nested WPF elements inside this stack panel -->
</StackPanel>
<ItemsControl Grid.Column="1" ItemsSource="{Binding Tasks}">
<ItemsControl.Template>
<ControlTemplate TargetType="{x:Type ItemsControl}" >
<ItemsPresenter/>
</ControlTemplate>
</ItemsControl.Template>
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate DataType="cc:Tasks">
<StackPanel Orientation="Vertical">
<TextBlock Text="{Binding Title}"></TextBlock>
</StackPanel>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</Grid>
I've read this thread, but I'm not entirely sure if that answer fits this problem.
Following #Paparazzi advice, I created a converter to bind the width of the scrollviewer. Here's how I did it:
In the C# code behind, I create the converter:
public class PercentageConverter : MarkupExtension, IValueConverter
{
private static PercentageConverter _instance;
#region IValueConverter Members
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
return System.Convert.ToDouble(value) * System.Convert.ToDouble(parameter);
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
#endregion
public override object ProvideValue(IServiceProvider serviceProvider)
{
return _instance ?? (_instance = new PercentageConverter());
}
}
Then, in my .xaml code, I use it like this:
<UserControl.Resources>
<ResourceDictionary>
<local:PercentageConverter x:Key="PercentageConverter"></local:PercentageConverter>
</ResourceDictionary>
</UserControl.Resources>
<Grid Name="ParentGrid">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"></ColumnDefinition>
<ColumnDefinition Width="Auto" ></ColumnDefinition>
</Grid.ColumnDefinitions>
<ScrollViewer Grid.Column="0"
HorizontalScrollBarVisibility="Auto"
MinWidth="200"
Width="{Binding Path=ActualWidth, ElementName=ParentGrid, Converter={StaticResource PercentageConverter}, ConverterParameter='0.6'}">
<StackPanel Orientation="Vertical" ScrollViewer.CanContentScroll="True">
<!-- Lots of other WPF Framework elements -->
</StackPanel>
</ScrollViewer>
<ItemsControl Grid.Column="1" ItemsSource="{Binding Tasks}">
<ItemsControl.Template>
<ControlTemplate TargetType="{x:Type ItemsControl}" >
<ItemsPresenter/>
</ControlTemplate>
</ItemsControl.Template>
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel></StackPanel>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate DataType="cc:Tasks">
<StackPanel Orientation="Vertical">
<TextBlock Text="{Binding Title}"></TextBlock>
</StackPanel>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</Grid>
The key is to define the converter in the ResourceDictionary. Then, I set the width property of the ScrollViewer element to bind to the actual width of the parent grid, using the converter to set it to a certain percentage (in this case 0.6 is the parameter or 60% of the overall width of the parent grid). I hope this helps someone in the future.
Related
The DataTemplate is used in an ItemsControl inside a UserControl. The UserControl is added multiple times inside a stackpanel. (pfew)
I need to be able to determine how many children the stackpanel has. I assumed this was possible using the FindAncestor mode, but I'm afraid I need your help.
Here's the XAML logic:
<StackPanel Name="BeforeTournament" Orientation="Horizontal" VerticalAlignment="Top">
<UserControl ...
<Grid>
<TextBlock Name="txtTitle" FontSize="14" />
<ItemsControl Name="MatchList" ItemsSource="{Binding Matches, Mode=OneWay}" Width="400" Margin="-7,20,0,0"
ScrollViewer.HorizontalScrollBarVisibility="Hidden"
ScrollViewer.VerticalScrollBarVisibility="Hidden">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Grid Name="MatchTemplate" Width="390"
Height="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type StackPanel}},
Path=(Children.Count * 300}"
Margin="0,0,0,50" VerticalAlignment="Center">
</Grid>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</Grid>
</UserControl>
//Duplicates below, same logic to determine width
<UserControl></UserControl>
</StackPanel>
So I would basically like to know how many UserControls have been added to the stackpanel and be able to use this amound of children to calculate the height of the grid inside the DataTemplate.
The FindAncestor relative source is giving me an error saying that Children is not supported in a relative context.
Ok, as I said in the comment there should be a better way to do this, but I'am pretty sure one way to do this is to use a converter. Pass the stackpanel as parameter and return the number of children multiplied by 300(If that is what you want)
I have tried this code and it works. Just for show i added two usercontrols manually. I have also tried putting the usercontrols in a seperate xaml file.
Main.xaml
<Window.Resources>
<local:StackpanelConverter x:Key="StackpanelConverter"/>
</Window.Resources>
<StackPanel Name="BeforeTournament" Orientation="Horizontal" VerticalAlignment="Top">
<UserControl>
<Grid Height="200" Background="Brown">
<TextBlock Name="txtTitle" FontSize="14" />
<ItemsControl Name="MatchList" ItemsSource="{Binding MyControls}" BorderBrush="Bisque" BorderThickness="10" Width="400" Margin="-7,20,0,0"
ScrollViewer.HorizontalScrollBarVisibility="Hidden"
ScrollViewer.VerticalScrollBarVisibility="Hidden">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Grid Name="MatchTemplate" Width="390" Background="Blue"
Height="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type StackPanel}}, Converter={StaticResource StackpanelConverter}}"
Margin="0,0,0,50" VerticalAlignment="Center">
</Grid>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</Grid>
</UserControl>
<UserControl>
<Grid Height="200" Background="Brown">
<TextBlock FontSize="14" />
<ItemsControl ItemsSource="{Binding MyControls}" BorderBrush="Bisque" BorderThickness="10" Width="400" Margin="-7,20,0,0"
ScrollViewer.HorizontalScrollBarVisibility="Hidden"
ScrollViewer.VerticalScrollBarVisibility="Hidden">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Grid Name="MatchTemplate" Width="390" Background="Blue"
Height="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type StackPanel}}, Converter={StaticResource StackpanelConverter}}"
Margin="0,0,0,50" VerticalAlignment="Center">
</Grid>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</Grid>
</UserControl>
</StackPanel>
Example of converter: (this is written in notepad so there might be errors)
public class StackpanelConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
var stackpanel = value as StackPanel;
var height = stackpanel.Children.Count;
return height*300;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
Tell me if I still don't understand the question :)
Assignement
Create an unknown number of labels with an ItemsControl on a resizeable window. The Labels should be on the right offSet on the ItemsControl.
Problem
I have trouble finding a way for my Labels to stretch with the window when it is re-sized to a larger format while keeping their offset to each other. My binding works perfectly. The offset also works properly, but now I need the labels to stretch along when the window is re-sized while keeping their relative distance from Canvas.Left.
Code
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="auto" />
</Grid.RowDefinitions>
<ItemsControl Grid.Row="0" ItemsSource="{Binding Path=Labels}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<Canvas IsItemsHost="True">
<Canvas.Background>
<SolidColorBrush Color="Black"/>
</Canvas.Background>
</Canvas>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemContainerStyle>
<Style>
<Setter Property="Canvas.Left" Value="{Binding Path=OffSet}"/>
</Style>
</ItemsControl.ItemContainerStyle>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Label Foreground="White" HorizontalAlignment="Left" Content="{Binding Path=Name}"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</Grid>
I couldn't find a solution and tried a lot. Any suggestions?
As I suggested in the comments, I think a better panel to use in this instance might be a UniformGrid. As long as you can work out in code what your degree values are going to be, you can make a collection of those that you can bind to.
XAML:
<Window x:Class="WpfApplication1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<ItemsControl ItemsSource="{Binding Degrees}" VerticalAlignment="Top">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<UniformGrid Rows="1" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Border BorderBrush="Black" BorderThickness="1" Margin="2">
<TextBlock Text="{Binding}" TextAlignment="Center" />
</Border>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</Window>
Code-behind:
using System.Linq;
using System.Windows;
namespace WpfApplication1
{
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
Degrees = Enumerable.Range(1, 5).Select(x => x * 10 + 100).ToArray();
DataContext = this;
}
public int[] Degrees { get; private set; }
}
}
As you will see when you resize the window, the individual elements resize to take up the appropriate widths.
I think this is the type of thing you're after but please comment if not and I'll try to improve my answer.
I have a situation where I want to see a queue of items related to another list. I want to arrange the parent items vertically and the child items horizontally. So Far I have the following:
Parent:
<ListBox x:Name="listResources" ItemsSource="{Binding Resources}" >
<ListBox.ItemTemplate>
<DataTemplate>
<local:ResourceControl x:Name="resources" thisResource="{Binding Path=.}" Margin="2"></local:ResourceControl>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
My Child items:
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition></ColumnDefinition>
<ColumnDefinition></ColumnDefinition>
</Grid.ColumnDefinitions>
<Grid.Resources>
<Style TargetType="ListBox">
<Setter Property="ItemsPanel">
<Setter.Value>
<ItemsPanelTemplate>
<StackPanel
Orientation="Horizontal"
VerticalAlignment="Center"
HorizontalAlignment="Center"
Margin="3">
</StackPanel>
</ItemsPanelTemplate>
</Setter.Value>
</Setter>
</Style>
</Grid.Resources>
<StackPanel Grid.Column="0">
<TextBlock FontSize="20" x:Name="labelResourceName" Text="{Binding ResourceName}"></TextBlock>
</StackPanel>
<ListBox Grid.Column="1" x:Name="listOperations" ItemsSource="{Binding Operations}" >
<ListBox.ItemTemplate>
<DataTemplate>
<local:OperationControl x:Name="operations" thisOperation="{Binding Path=.}" Margin="2" ></local:OperationControl>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Grid>
This works relatively well and gives me my child items listed horizontally as I want them to be. The problem is that I need to be able to scroll the child items individually. As it is written currently I have the ability to scroll horizontally, but all of the child items scroll together.
I have attempted to wrap the parent listbox inside a but that did not seem to do it either.
How can I get the items inside of the listbox to scroll individually instead of all together as a group?
I found the answer to this issue. The problem was that I had not defined a width for the listboxes. Once I added widths to the controls the scrolling worked properly.
I have a ListView in a FlipView
<FlipView
x:Name="flipView"
AutomationProperties.AutomationId="ItemsFlipView"
AutomationProperties.Name="Item Details"
TabIndex="1"
Width="Auto"
Grid.Row="2"
Grid.Column="1"
VerticalAlignment="Top"
HorizontalAlignment="Center"
ItemsSource="{Binding Source={StaticResource itemsViewSource}}" Padding="0" VirtualizingStackPanel.VirtualizationMode="Standard">
<FlipView.ItemTemplate>
<DataTemplate>
<!--
UserControl chosen as the templated item because it supports visual state management
Loaded/unloaded events explicitly subscribe to view state updates from the page
-->
<UserControl Loaded="StartLayoutUpdates" Unloaded="StopLayoutUpdates">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<StackPanel Grid.Column="1" Orientation="Vertical" Margin="0,100,0,0">
<ListView x:Name="ListofOptions" Height="400" Width="280"
ItemsSource="{Binding QuestionOptions}" SelectedValue="{Binding Answer,Mode=TwoWay}"
IsEnabled="{Binding IsEnabled,Mode=TwoWay}" >
<ListView.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal" >
<StackPanel.Resources>
<common:AltBackgroundConverter x:Key="BGConvertor" />
</StackPanel.Resources>
<StackPanel.Background>
<SolidColorBrush Color="{Binding IndexWithinParentCollection, Mode=OneWay, Converter={StaticResource BGConvertor}}"></SolidColorBrush>
</StackPanel.Background>
<TextBlock Text="{Binding OptionValue}" />
</StackPanel>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</StackPanel>
</Grid>
</UserControl>
</DataTemplate>
</FlipView.ItemTemplate>
</FlipView>
I write a value conventor of ListView for changing background of alternative row. here is Conventor's code
public class AltBackgroundConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, string language)
{
if (!(value is int)) return null;
int index = (int)value;
if (index % 2 == 0)
return Colors.White;
else
return Colors.LightGray;
}
// No need to implement converting back on a one-way binding
public object ConvertBack(object value, Type targetType, object parameter, string language)
{
throw new NotImplementedException();
}
}
when the listbox is out of FlipView everything is Ok but the Conventor doesn't execute when the ListView is in a FlipView. please advice me.
Created a new Split XAML project in VS2012 and added your converter there and used it in ListView and it was still working after moving the ListView inside a FlipView.
I'm now guessing it's a binding issue, happening because root binding object has changed and one of the bindings not resolved as we expect. have you tried moving the Resources tag to upper level which is the FlipeView?
P.S. This is more of a comment, but I don't have reputation for comments!
I have a ListBox control that has been assigned to Grid.Column 0 which has a value of '*' defined for it's width, however when rendered there is a sizeable amount of space that is not being used.
I have also noticed that there is a border of sorts around the ListBox control itself, however I have not added one within the markup.
My UI (Areas of concern marked in Red):
My Markup:
<Window.Resources>
<DataTemplate x:Key="GameImagesTemplate" >
<StackPanel Orientation="Vertical">
<Image Source="{Binding FileInfo.FullName}" Margin="8,8,8,8" Height="70" Width="70" />
<Label Content="{Binding Name}" Width="70" />
</StackPanel>
</DataTemplate>
<DataTemplate x:Key="GameTemplate">
<StackPanel>
<Label Content="{Binding Name}" Background="Gray" FontSize="16" />
<ListBox x:Name="imageContent" ItemsSource="{Binding FileList}" ItemTemplate="{StaticResource GameImagesTemplate}" ScrollViewer.HorizontalScrollBarVisibility="Disabled" >
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel Orientation="Horizontal" IsItemsHost="True" />
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
</ListBox>
</StackPanel>
</DataTemplate>
</Window.Resources>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<ListBox x:Name="ContentList" ItemTemplate="{StaticResource GameTemplate}" Grid.Column="0" ScrollViewer.HorizontalScrollBarVisibility="Disabled" />
<StackPanel Grid.Column="1" Background="DarkGray">
<Button Click="OnLoad">_Load</Button>
<Button Click="OnSave">_Save</Button>
<Button Click="OnAdd">_Add</Button>
<Button Click="OnDelete">_Delete</Button>
</StackPanel>
</Grid>
How would I go about resolving both of the issues raised. Is it the default behaviour of the ListBox control?
Many thanks
Yes, that is the default behavior.
In the case of the alignment it looks like you have a WrapPanel in each ListBoxItem which doesn't have quite enough space to put another item on line 1. The remaining space is unused because of the HorizontalContentAlignment setting on ListBox defaulting to Left. This setting is in turn bound to by the default ListBoxItem. Setting HorizontalContentAlignment="Stretch" on your ListBox should fix that.
The outer border comes from the default setting for BorderBrush and BorderThickness. Setting BorderThickness="0" will get rid of it entirely.
There are some other default Padding settings that add some spacing in the default Styles and Templates. If you want to get more into those add a ListBox to a project in Blend and make a copy of its default Template and ItemContainerStyle and check out the XAML. Also consider using the base ItemsControl in cases where you don't need selection behavior, as it doesn't have any of these type of default settings.