Populating ItemsControl Using Uniform Grid as ItemsPanel Column First - c#

I have an ObservableCollection of 24 Things.
private ObservableCollection<Thing> things;
public ObservableCollection<Thing> Things
{
get => things;
set
{
things= value;
RaisePropertyChanged();
}
}
I also have a selected Thing
private Thing selectedThing;
public Thing SelectedThing
{
get => selectedThing;
set
{
selectedThing= value;
RaisePropertyChanged();
}
}
I need to display these items in a grid. I am creating a grid of buttons, each with a command and a command parameter that allows me to set selected Thing from the collection.
I need to populate this grid COLUMNS first. I.E:
Is there a way to do this in WPF Using an ItemsControl and a Uniform grid?
<ItemsControl ItemsSource="{Binding Things}" HorizontalAlignment="Center" Margin="20">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<UniformGrid Columns="3" Rows="8" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Button Content="{Binding ThingPosition}"
Height="30"
Width="80"
Margin="3"
FontSize="8"
Command="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type ItemsControl}}, Path=DataContext.SelectThingCommand}"
CommandParameter="{Binding Path=.}"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>

In case a simple reordering of the elements in the ItemsSource collection is not possible, the following LayoutTransforms should do the job:
<ItemsControl ...>
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<UniformGrid Rows="3">
<UniformGrid.LayoutTransform>
<TransformGroup>
<RotateTransform Angle="-90"/>
<ScaleTransform ScaleY="-1"/>
</TransformGroup>
</UniformGrid.LayoutTransform>
</UniformGrid>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemContainerStyle>
<Style TargetType="ContentPresenter">
<Setter Property="LayoutTransform">
<Setter.Value>
<TransformGroup>
<ScaleTransform ScaleY="-1"/>
<RotateTransform Angle="90"/>
</TransformGroup>
</Setter.Value>
</Setter>
</Style>
</ItemsControl.ItemContainerStyle>
<ItemsControl.ItemTemplate>
...
</ItemsControl.ItemTemplate>
</ItemsControl>

Related

How to draw evenly spaced diagonal text with wpf xaml

I am doing a custom WPF UserControl and i need to draw a variable size text that is rotated 45 degrees and spaced evenly horizontally, like the next image (being the red bars the text):
With the following code:
<UserControl.Resources>
<ResourceDictionary>
<DataTemplate x:Key="CheckTemplate">
<!-- description -->
<TextBlock
VerticalAlignment="Bottom" Margin="-10,0,0,0" Text="{Binding Check.Name}" Background="Transparent" x:Name="AAA">
<TextBlock.LayoutTransform>
<RotateTransform Angle="-45" />
</TextBlock.LayoutTransform>
</TextBlock>
<DataTemplate.Triggers>
<Trigger Property="ItemsControl.AlternationIndex" Value="1">
<Setter Property="Margin" Value="0" TargetName="AAA" />
</Trigger>
</DataTemplate.Triggers>
</DataTemplate>
<ItemsPanelTemplate x:Key="ChecksItemsPanel">
<StackPanel Orientation="Horizontal"
HorizontalAlignment="Left"
VerticalAlignment="Bottom"
/>
</ItemsPanelTemplate>
</ResourceDictionary>
</UserControl.Resources>
<StackPanel x:Name="RootPanel" Margin="5">
<ItemsControl
x:Name="WorkflowChecksItemsControl"
ItemTemplate="{DynamicResource CheckTemplate}"
ItemsPanel="{DynamicResource ChecksItemsPanel}"
ItemsSource="{Binding WorkflowChecks}" />
</StackPanel>
i only managed to do something like this:
How can i do this using XAML?
In this project i am also using Telerik UI for WPF, and i can use theirs framework if it is simpler.
You may combine a -90° LayoutTransform of the ItemsPanel with a 45° RenderTransform of each TextBlock. For the horizontal distance, simply set the TextBlocks' Height.
<ItemsControl ItemsSource="{Binding WorkflowChecks}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel>
<StackPanel.LayoutTransform>
<RotateTransform Angle="-90"/>
</StackPanel.LayoutTransform>
</StackPanel>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Check.Name}" RenderTransformOrigin="0,1">
<TextBlock.RenderTransform>
<RotateTransform Angle="45"/>
</TextBlock.RenderTransform>
</TextBlock>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
Result:

How do I add a Label over a ListBox in WPF?

I have currently a ListBox in a DockPanel (that represents a wonderful Diaballik game) and at the end I want to be able to show up a label Victory over the grid made by the boxes like in the picture :
<DockPanel>
<ListBox DockPanel.Dock="Top" ItemsSource="{Binding Cases}" SelectedItem="{Binding SelectedCase, Mode=TwoWay}" HorizontalAlignment="Center" VerticalAlignment="Top" SnapsToDevicePixels="True">
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<UniformGrid Columns="{Binding Size}"/>
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
<ListBox.ItemContainerStyle>
<Style TargetType="ListBoxItem">
<Setter Property="Padding" Value="0"/>
</Style>
</ListBox.ItemContainerStyle>
<ListBox.ItemTemplate>
<DataTemplate>
<Grid >
<Ellipse Fill="{Binding Color}" Width="50" Height="50" Visibility="{Binding HasPawn, Converter={StaticResource bool2visibility}}"/>
...
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
<Label Content="VICTORY" /> <!-- How can this go over the list box ? -->
</DockPanel>
You could do something like this (the behaviour of a Grid is such that it will just "stack" the items "on top" of each other):
<DockPanel>
<Grid DockPanel.Dock="Top">
<ListBox ItemsSource="{Binding Cases}" SelectedItem="{Binding SelectedCase, Mode=TwoWay}" HorizontalAlignment="Center" VerticalAlignment="Top" SnapsToDevicePixels="True">
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<UniformGrid Columns="{Binding Size}"/>
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
<ListBox.ItemContainerStyle>
<Style TargetType="ListBoxItem">
<Setter Property="Padding" Value="0"/>
</Style>
</ListBox.ItemContainerStyle>
<ListBox.ItemTemplate>
<DataTemplate>
<Grid >
<Ellipse Fill="{Binding Color}" Width="50" Height="50" Visibility="{Binding HasPawn, Converter={StaticResource bool2visibility}}"/>
...
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
<Label Content="VICTORY" HorizontalAlignment="Center" VerticalAlignment="Center" /> <!-- How can this go over the list box ? -->
</Grid>
</DockPanel>

ItemsControl with none background WPF

I would like to ask you, if is possible to have ItemsControl without background (x:null), not transparent.
I have collection with data, and these are showed in ItemsControl with help of DataTemplate. Some data in datatemplate are collapsed, and I need to be able to clickable on another control behind the itemscontrol.
Here is example what I mean:
<Button x:Name="bt_behind"></Button>
<ItemsControl ItemsSource="{Binding ListOfData}" Background="{x:Null}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal" Background="{x:Null}"></StackPanel>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate DataType="{x:Type Class:Data}">
<Grid Width="100" Background="{x:Null}">
<Rectangle x:Name="rec" Fill="Red" Height="100" Visibility="Collapsed">
</Grid>
<DataTemplate.Triggers>
<DataTrigger Binding="{Binding IsVisible}" Value="true">
<Setter TargeName="rec" Property="Visibility" Value="Visible"/>
</DataTrigger>
<DataTemplate.Triggers>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
Example where item3 is partly collapsed and marked area, where is empty place
I set everywhere the background to null (try itemcontainerstyle too), but without success. On button behind ItemsControl still is not clickable. I think that ItemsControl have transparent background for events, but is it possible to remove this background?
Thanks for any advice and sorry for my english:)
-pav-
Well, as i said, it's all working. Fixed XAML:
<Grid>
<Button x:Name="bt_behind" Content="behind" Click="Bt_behind_OnClick"/>
<ItemsControl ItemsSource="{Binding ListOfData}" Background="{x:Null}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal" Background="{x:Null}"></StackPanel>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate DataType="{x:Type local:Data}">
<Grid Width="100" Background="{x:Null}">
<Rectangle x:Name="rec" Fill="Red" Height="100" Visibility="Collapsed"/>
</Grid>
<DataTemplate.Triggers>
<DataTrigger Binding="{Binding IsVisible}" Value="true">
<Setter TargetName="rec" Property="Visibility" Value="Visible"/>
</DataTrigger>
</DataTemplate.Triggers>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</Grid>
Just for test.
private void Bt_behind_OnClick(object sender, RoutedEventArgs e)
{
MessageBox.Show("");
}

DataBinding Issue while using MultiDataTrigger

Its looks simple and i tried all possible ways i know to fix the error still no luck, looks like am missing something.
Here is my code. at least the relevent part
<ItemsControl ItemsSource="{Binding Source}" >
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<Grid/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<VirtualizingStackPanel Orientation="Horizontal">
<ContentControl>
<Path x:Name="Bound" Stroke="Black">
<Path.Style>
<Style>
<Style.Triggers>
<MultiDataTrigger>
<MultiDataTrigger.Conditions>
<Condition Binding="{Binding Condition1}"
Value="true"/>
<Condition Binding="{Binding Condition2}"
Value="false"/>
</MultiDataTrigger.Conditions>
<Setter Property="Path.Data">
<Setter.Value>
<RectangleGeometry Rect="{Binding Rect1}"/>
</Setter.Value>
</Setter>
<Setter Property="Path.Fill">
<Setter.Value>
<VisualBrush>
<VisualBrush.Visual>
// Here is the Problem
<TextBlock Text="{Binding Number}"
Width="50"
Height="30"
Background="White" />
// Binding is not working
</VisualBrush.Visual>
</VisualBrush>
</Setter.Value>
</Setter>
</MultiDataTrigger>
</Style.Triggers>
</Style>
</Path.Style>
</Path>
</ContentControl>
</VirtualizingStackPanel>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
TextBlock in the visualBrush is not getting the value
'Number'
If i remove all the Triggers then everything work fine. somehow there is break in the binding.
That's because the VisualBrush doesn't have a DataContext. You have to use some proxy element.
Define your proxy element:
public class DataContextProxy: Freezable
{
public DataContextProxy()
{
BindingOperations.SetBinding(this, DataContextProperty, new Binding());
}
public object DataContext
{
get { return GetValue(DataContextProperty); }
set { SetValue(DataContextProperty, value); }
}
public static readonly DependencyProperty DataContextProperty = FrameworkElement
.DataContextProperty.AddOwner(typeof (DataContextProxy));
protected override Freezable CreateInstanceCore()
{
return new DataContextProxy();
}
}
Add it to some parent's Resources that has the DataContext you need:
<ItemsControl ItemsSource="{Binding Source}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<Grid/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<VirtualizingStackPanel Orientation="Horizontal">
<ContentControl>
<ContentControl.Resources>
<behavior:DataContextProxy x:Key="Proxy"
DataContext="{Binding}" />
</ContentControl.Resources>
<Path x:Name="Bound" Stroke="Black">
...
And then bind your TextBlock's DataContext to the proxy's DataContext:
...
<Setter Property="Path.Fill">
<Setter.Value>
<VisualBrush>
<VisualBrush.Visual>
<TextBlock DataContext="{Binding Source={StaticResource Proxy},
Path=DataContext}"
Text="{Binding Number}"
Width="50"
Height="30"
Background="White" />
</VisualBrush.Visual>
</VisualBrush>
</Setter.Value>
</Setter>
...
Haven't personally tried it, but should work... Comment if it doesn't!
Cheers.
Freezable objects likes VisualBrush is not part of the element tree(neither logical tree or VisualTree. so you have to get the datacontext and bind to Visual Property of VisualBrush.
Assuming number is from ViewModel Change your code as follows:
<TextBlock Text="{Binding Path=Number}" Width="50" Height="30" Background="White" />

wpf borderbrush binding in itemscontrol

I have a Border inside an ItemsControl which needs to get it's BorderBrush from a field called BorderColor in the ItemsSource (an Observable Collection).
<ItemsControl ItemsSource="{Binding ObsColOfThings}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemContainerStyle>
<Style TargetType="ContentPresenter">
<Setter Property="ContentTemplate">
<Setter.Value>
<DataTemplate>
<Border BorderThickness="3" BorderBrush="{Binding BorderColor}">
<ContentPresenter Content="{Binding}" />
</Border>
</DataTemplate>
</Setter.Value>
</Setter>
</Style>
</ItemsControl.ItemContainerStyle>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Border >
<TextBlock Text="{Binding Text}" />
</Border>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
The binding is working but the border brush is not.
I've also tried this and it doesn't work either despite the text working fine
<ItemsControl ItemsSource="{Binding Appointments}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Border BorderBrush="{Binding BorderColor" BorderThickness="3">
<TextBlock Text="{Binding AppointmentDescription}" />
</Border>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
Where am I going wrong?
It doesn't make sense to specify the ItemTemplate and the ContentPresenter's ContentTemplate of an ItemsControl at the same time, because the ItemTemplate effectively replaces the ContentTemplate.
Drop the entire ItemContainerStyle and write your ItemsControl like this:
<ItemsControl ItemsSource="{Binding ObsColOfThings}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Border BorderThickness="3" BorderBrush="{Binding BorderColor}">
<TextBlock Text="{Binding Text}" />
</Border>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
The type of the BorderColor property should be Brush:
public class Item
{
public string Text { get; set; }
public Brush BorderColor { get; set; }
}
and then it should of course be called BorderBrush, not BorderColor.

Categories