ListView for timetable - wpf

I'm developing an app that presents a timetable, that looks like this:
However, I have several problems with it:
When there are too many items (minutes) to display in 1 row it
should be broken automatically in several rows (e.g. all rows after
06 should be wrapped)
I don't know where the spacing around minute items comes from. It's not
item`s margin.
When scrolling the timetable list with finger, it only gets scrolled if no minute box is touched. Otherwise the minute box moves a bit, not the
entire timetable list.
The timetable list is bound to an ObservableCollection of TimetableHour instaces:
public class TimetableHour
{
public sbyte Hour { get; set; }
public IList<TimetableItem> Items { get; set; }
public string HourString
{
get { return Hour.ToString("00") + ":"; }
}
}
and the XAML page:
<Style TargetType="ListView" x:Key="TimetableListViewStyle">
<Setter Property="ScrollViewer.VerticalScrollBarVisibility" Value="Hidden" />
<Setter Property="Margin" Value="0,0,60,0" />
<Setter Property="ItemTemplate">
<Setter.Value>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBox Text="{Binding HourString}"
Width="60" Height="50"
TextAlignment="Center"
Background="CornflowerBlue" Foreground="White" BorderThickness="0"
FontSize="23"
Padding="10">
</TextBox>
<GridView ItemsSource="{Binding Items}" Height="Auto" SelectionMode="None" IsTapEnabled="False" IsHoldingEnabled="False" >
<GridView.ItemsPanel>
<ItemsPanelTemplate>
<WrapGrid Orientation="Horizontal" />
</ItemsPanelTemplate>
</GridView.ItemsPanel>
<GridView.ItemTemplate>
<DataTemplate>
<StackPanel Background="LightSkyBlue" Width="60" Height="50">
<TextBlock Text="{Binding Minute}" HorizontalAlignment="Center" FontSize="17" FontWeight="Medium" Margin="0,2,0,0"></TextBlock>
<TextBlock HorizontalAlignment="Center">Tip</TextBlock>
</StackPanel>
</DataTemplate>
</GridView.ItemTemplate>
</GridView>
</StackPanel>
</DataTemplate>
</Setter.Value>
</Setter>
</Style>
<ListView Grid.Row="2" Grid.Column="2" ItemsSource="{Binding Timetable}"
Style="{StaticResource TimetableListViewStyle}"
SelectionMode="None" />

Ok, so I figured them out:
To make the minutes items wrap I needed to specify the exact width
of the GridView that is used to display them. Alternatively I can use a Grid instead of a StackPanel, to allow to use all available space - see GridView width inherited from parent
To control the spacing around GridView items you need to define the
ItemContainerStyle as described here:
Windows8 ListView and space between items
To achieve this I can set IsSwipeEnabled="False" on the GridView.

Related

WPF ListView/Gridview allow users to select multiple items and group them together

I have a WPF ListView/GridViwe in a MVVM application. GridView is bound to a List in the ViewModel.
The requirement is that the users should be able to select multiple rows of the gridview, right-click on it and see a context menu "Group These Together". Once selected, all these items should be collapsed into one group with a expander or + sign added at the beginning.
Can somebody please help me in getting this working?
I was working on similar problem, but I had to group items by their let's say name. What I've done was create DataTrigger or MultiDataTrigger depending on your data requirements and then when conditions are true i.e. item selected change the container for GroupItem. What I mean is when you create your list view, you have to create it with grouped view, which btw is not grouped as you can declare it without expander and just use the StackPanel. After that you need 3 lines of Code to set the grouping. Here is an example for you:
MAIN.xaml
<ListView
ScrollViewer.CanContentScroll="False"
x:Name="lsvProducts"
ItemsSource="{Binding Products, NotifyOnSourceUpdated=True}"
MouseDown="lsvProducts_MouseDown">
<ListView.View>
<GridView>
<GridViewColumn Width="Auto" Header="Code" DisplayMemberBinding="{Binding ID}"/>
<GridViewColumn Width="Auto" Header="Description" DisplayMemberBinding="{Binding Desc}"></GridViewColumn>
<GridViewColumn Width="Auto" Header="Qty" DisplayMemberBinding="{Binding Qty}"></GridViewColumn>
</GridView>
</ListView.View>
<ListView.GroupStyle>
<GroupStyle ContainerStyle="{StaticResource GroupedView}"/>
</ListView.GroupStyle>
</ListView>
As you can see I have declared an empty container for the grouped style, reson why is because you can't assign it without previous declaration. After this declaration you need this in your generic.xaml
<Style x:Key="GroupedView" TargetType="{x:Type GroupItem}">
<Style.Triggers>
<DataTrigger Binding="{Binding ElementName=chbx, Path=IsChecked}" Value="True">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate>
<Expander IsExpanded="False">
<Expander.Header>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Name}" FontWeight="Bold" Foreground="Gray" FontSize="22" VerticalAlignment="Bottom" />
<TextBlock Text="{Binding ItemCount}" FontSize="16" Foreground="DimGray" FontWeight="Bold" FontStyle="Italic" Margin="10,0,0,0" VerticalAlignment="Bottom" />
<TextBlock Text=" item(s)" FontSize="16" Foreground="Silver" FontStyle="Italic" VerticalAlignment="Bottom" />
</StackPanel>
</Expander.Header>
<ItemsPresenter />
</Expander>
</ControlTemplate>
</Setter.Value>
</Setter>
</DataTrigger>
After you declared your style for the true value you need to declare one for without expander
<!--This goes in the same style as the prevoius sample code -->
<DataTrigger Binding="{Binding ElementName=chbx, Path=IsChecked}" Value="False">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate>
<StackPanel>
<ItemsPresenter />
</StackPanel>
</ControlTemplate>
</Setter.Value>
</Setter>
</DataTrigger>
Next step is to define the grouping:
//lsvProducts is our ListView
view = (CollectionView) CollectionViewSource.GetDefaultView(lsvProducts.ItemsSource);
//in the creation parameter you should put field that you want to group by :-)
PropertyGroupDescription grouping = new PropertyGroupDescription("FieldToGroupBy");
view.GroupDescriptions.Add(grouping);
Which should apper in your ViewModel.
Good luck!
Let us know if you need any more help, I'll try my best with my English ;-)
EDIT
I forgot to mention when you assign the grouping you need to check the number of groups already in the collection and only apply it when there is non or 0, otherwise you'll apply the grouping multiple times and the expander will be repeeated in the result window as many times as you added the grouping :-)
What in my mind to select multiple row Ctrl+click
It will select multiple row and on right click open a context menu after setting its Isopen to true
You should also bind selected item to list and Use this item to make an expander

wpf grid as itemspaneltemplate gives inconsistent heights

im trying to make a week scheduler
so for each day i have a listbox with a grid as its itemspaneltemplate , and each individual session gets its row and rowspan from the database and its working very well
the problem is when i have a short appointment so the rowspan is only 2 or 3 and the content is longer, in that case the rows containing the appointment grow to accommodate the larger content
i don't want that, id rather the appointments reflect their time span even if i don't see all data
i tried many combinations of answers that i found online
heres the whole markup
<ListBox Name="lsbTasks" Loaded="lsbTasks_Loaded_1" HorizontalContentAlignment="Stretch" ScrollViewer.HorizontalScrollBarVisibility="Disabled" VerticalContentAlignment="Stretch" >
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<Grid Initialized="Grid_Initialized_1" Background="Thistle" IsSharedSizeScope="True"/>
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
<ItemsControl.ItemContainerStyle>
<Style>
<Setter Property="Grid.Row" Value="{Binding BusyFrom,Converter={StaticResource BusyFromConverter}}" />
<Setter Property="Grid.RowSpan" Value="{Binding BusyMinutes,Converter={StaticResource BusyMinutesConverter}}" />
<Setter Property="ScrollViewer.VerticalScrollBarVisibility" Value="Auto" />
</Style>
</ItemsControl.ItemContainerStyle>
<ListBox.ItemTemplate>
<DataTemplate>
<ScrollViewer ScrollViewer.CanContentScroll="True" ScrollViewer.VerticalScrollBarVisibility="Auto" ScrollViewer.IsDeferredScrollingEnabled="True">
<StackPanel Background="Purple" PreviewMouseDown="DockPanel_PreviewMouseDown_1">
<TextBlock TextAlignment="Center" TextWrapping="Wrap" Text="{Binding SessionPlace}" Foreground="Thistle" DockPanel.Dock="Bottom" />
<TextBlock TextAlignment="Center" TextWrapping="Wrap" Text="{Binding ClientName}" Foreground="White" FontWeight="Bold" DockPanel.Dock="Top" />
<TextBlock TextAlignment="Center" TextWrapping="Wrap" Text="{Binding OppositionName}" Foreground="White" DockPanel.Dock="Top" />
<TextBlock TextAlignment="Center" TextWrapping="Wrap" Text="{Binding SubjectName}" Foreground="Thistle" />
</StackPanel>
</ScrollViewer>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
and heres the initialization code
Private Sub Grid_Initialized_1(sender As Grid, e As EventArgs)
Dim mnts = MyWorkDaySpan.TotalMinutes
For i = 1 To mnts / 10
Dim rd = New RowDefinition With {.Height = New GridLength(1, GridUnitType.Star)}
'rd.SharedSizeGroup = "RowGroup"
sender.RowDefinitions.Add(rd)
Next
End Sub
but still the rows are uneven, as proven by the gridlines
id appreciate any help or guidance
You should use a different Panel, perhaps a Canvas and bind the Canvas.Top and Height properties in the ListBoxItem style.
Another alternative would be to write a custom Panel class ("CalendarPanel"), which defines two attached properties BusyFrom and BusyMinutes and arranges its child elements according to the values of these properties.

WPF styling . Have no idea how to do it

I am trying to make an image button in WPF based on the following article
Custom button template in WPF
The problem with this is that I have No Idea how to make the image to become grayscale when disabling the button. So I am loading two images, But I can not load a second images into ButtonProperties.
So my idea was to load an rgb and greyscale image, and as I disable the button one image is hidden and another shown, and vice versa. and also the text to get greyed out. As I think, this coulde be done very nicely with triggers.
Here is my code:
a) style
<Setter Property="ContentTemplate">
<Setter.Value>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<Image Width="20"
Name="imgEnabled"
Source="{Binding Path=(ib:ButtonProperties.Image), RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Button}}}"></Image>
<Image Width="20"
Name="imgDisabled"
Source="{Binding Path=(ib:ButtonProperties.Image), RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Button}}}"></Image>
<ContentPresenter Content="{Binding Path=Content, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Button}}}"></ContentPresenter>
</StackPanel>
</DataTemplate>
</Setter.Value>
</Setter>
b) code:
public class ButtonProperties:DependencyObject
{
public static readonly DependencyProperty ImageProperty =
DependencyProperty.Register("Image",
typeof(ImageSource),
typeof(ButtonProperties),
new UIPropertyMetadata((ImageSource)null));
public static ImageSource GetImage(DependencyObject obj)
{
return (ImageSource)obj.GetValue(ImageProperty);
}
public static void SetImage(DependencyObject obj, ImageSource value)
{
obj.SetValue(ImageProperty, value);
}
}
3) control:
<ItemsControl Name="icImageButtons"
ItemsSource="{Binding}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Button ToolTip="{Binding Tip}"
ib:ButtonProperties.Image="{Binding EnabledSource}"
Content="{Binding Text}"
Style="{StaticResource ImageButton}" />
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
Any suggestions or ideas. I am just trying to get the effect when I disable the ImageButton, The image gets greyedOut as well as the text.
Suggestions?

How to create a standard DataTemplate for DataGridTemplateColumn?

I've got a DataTemplate for a DataGridTemplateColum wich looks like this:
<toolkit:DataGridTemplateColumn x:Name="DataGridTextColumnIstVorvorjahr" IsReadOnly="True" SortMemberPath="SummeIstVorvorjahr">
<toolkit:DataGridTemplateColumn.CellTemplate >
<DataTemplate>
<Grid HorizontalAlignment="Stretch" Background="Transparent" Margin="0,-5">
<DockPanel VerticalAlignment="Stretch" HorizontalAlignment="Stretch">
<TextBlock Panel.ZIndex="100" Style="{DynamicResource CellText}" Text="{Binding Path=SummeIstVorvorjahrGerundet, Converter={StaticResource numberFormatter}, ConverterParameter='#,0.0 T€'}" DockPanel.Dock="Right"/>
<Image Panel.ZIndex="90" DockPanel.Dock="Left" MouseLeftButtonUp="FilterDataGridAnalyse_MouseDoubleClick" HorizontalAlignment="Left" Margin="5,0,0,0" Width="20" Height="20" Visibility="Hidden" Name="ImageNormal" Source="pack://application:,,,/Cis.Common.Presentation;component/Resources/Images/Lupe.png" />
</DockPanel>
</Grid>
<DataTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="true">
<Setter TargetName="ImageNormal" Property="Visibility" Value="Visible" />
</Trigger>
</DataTemplate.Triggers>
</DataTemplate>
</toolkit:DataGridTemplateColumn.CellTemplate>
<toolkit:DataGridTemplateColumn.HeaderTemplate>
<DataTemplate >
<DockPanel VerticalAlignment="Stretch" HorizontalAlignment="Stretch" LastChildFill="False">
<TextBlock x:Name="TextBlockHeaderZeile1" Text="Ist" DockPanel.Dock="Top" />
<WrapPanel DockPanel.Dock="Top">
<TextBlock x:Name="TextBlockHeaderZeile2" Text=""/>
<ContentPresenter x:Name="contentPresenter">
<ContentPresenter.Content>
<Binding RelativeSource="{RelativeSource TemplatedParent}" Path="Content" />
</ContentPresenter.Content>
</ContentPresenter>
</WrapPanel>
<Border Style="{DynamicResource borderline}">
<TextBlock VerticalAlignment="Stretch" x:Name="TextBlockSumme" Text="{Binding Path=KumulierteSummeIstVorvorjahr, Converter={StaticResource numberFormatter}, ConverterParameter='#,0.0 T€', RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type cis:ChildWindow}}}"
/>
</Border>
</DockPanel>
</DataTemplate>
</toolkit:DataGridTemplateColumn.HeaderTemplate>
</toolkit:DataGridTemplateColumn>
Now I want to make a StandartTemplate for this Type because I've got many Colums like this, with only differ in the bindings of the texts in the colums as well as in their headers.
As far I've tried to make a Style for this, but this won't work, I tried to create an usercontrol (but I think it's like taking a sledgehammer to crack a nut).
So any help or hint how to solve this problem would be appreciated.
I don't see why you've rejected the UserControl approach. UserControls are pretty lightweight. They add very little overhead at runtime. They are an extra feature in your project of course, but I usually find that to be an improvement - WPF projects with a small number of large Xaml files are typically hard to maintain.
Far from being a 'sledgehammer', they seem like exactly the right solution here to me.
Add the DataTemplate into the Resources and then access it via a StaticResource
<Window>
<Window.Resources>
<DataTemplate x:Key="MyColumnTemplate">
...
</DataTemplate>
<DataTemplate x:Key="MyColumnTemplateHeader">
...
</DataTemplate>
</Window.Resources>
...
<toolkit:DataGridTemplateColumn x:Name="DataGridTextColumnIstVorvorjahr" IsReadOnly="True" SortMemberPath="SummeIstVorvorjahr"
CellTemplate={StaticResource MyColumnTemplate}
HeaderTemplate={StaticResource MyColumnTemplateHeader}
...
</Window>
If I understand you, you try to bind the same column template with different data and to have different header's content relative with column data. So, you may use "dynamic XAML" (XAML used in C# code - that is dynamic) which allows you to use one template for different data.
Here a simple example.
In C# code we create DataGridTemplateColumn object:
DataGridTemplateColumn tc = new DataGridTemplateColumn();
Then we set the CellTemplate property with template which is created dynamically in special function:
tc.CellTemplate = (DataTemplate)XamlReader.Parse(GetTextCellDataTemplate(someText));
Here is a special function which creates our template:
public static string GetTextCellDataTemplate(string bindingPath)
{
return #"
<DataTemplate
xmlns=""http://schemas.microsoft.com/winfx/2006/xaml/presentation""
xmlns:x=""http://schemas.microsoft.com/winfx/2006/xaml"" >
<ScrollViewer MaxHeight=""200"" MaxWidth=""250"" VerticalScrollBarVisibility=""Auto"">
<TextBlock Text=""{Binding Path=" + bindingPath + #"}""
TextWrapping=""Wrap"" />
</ScrollViewer>
</DataTemplate>";
}
Now you may send various information in this function as text and get the same template. You may choose the template from the information you want to put in the cell. For this you must write various function which will return various templates.
The same approach can be applied to header template.

Silverlight 4: Listbox doesn't shrink when its items shrink

from this question, I drilled down the problem to a listbox, that doesn't resize, when the Listbox-Items shrink. It resizes accordingly, when the size of the items grow, but it doesn't shrink, when the size of the items decrease.
The items can grow/shrink because the items containing textboxes, that resize with the input.
Jeremiah suggested to start a new question with more code to show, so here we go:
Our evil listbox is part of a UserControl, that contains a StackPanel with a Label (HorizontalAlignment=Center), the listbox (HA=Left) and a Button (HA=Right). The listbox-items are datalinked to an ObservableCollection
You will recognize beautiful BackgroundColors on the ListBox and the ListBoxItems. I used them to be able to tell wheter the Items or the Listbox itself doesn't shrink. I found out, that the Items shrink, but the Listbox doesn't.
Ok, here is the code of my UserControl:
<StackPanel VerticalAlignment="Top" HorizontalAlignment="Left">
<StackPanel.Background>
<SolidColorBrush Color="{StaticResource ColorBasicDark}"/>
</StackPanel.Background>
<sdk:Label x:Name="LabelServiceName" FontSize="{StaticResource FontSizeMedium}" Margin="2" HorizontalAlignment="Center" Content="LabelServiceName">
<sdk:Label.Foreground>
<SolidColorBrush Color="{StaticResource ColorBasicLight}"/>
</sdk:Label.Foreground>
</sdk:Label>
<ListBox x:Name="ListBoxCharacteristics" BorderBrush="{x:Null}" Margin="0" HorizontalContentAlignment="Left" FontSize="9.333" HorizontalAlignment="Left">
<ListBox.Foreground>
<SolidColorBrush Color="{StaticResource ColorBasicLight}"/>
</ListBox.Foreground>
<!-- DataTemplate to display the content -->
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel x:Name="StackPanelBorder" Orientation="Horizontal" HorizontalAlignment="Left">
<TextBox x:Name="TextBoxCharacteristicName" Style="{StaticResource InputTextBox}" Text="{Binding Name}" />
<TextBox x:Name="TextBoxSep" Style="{StaticResource ReadOnlyTextBox}" Text="=" />
<TextBox x:Name="TextBoxFuncOrValue" Style="{StaticResource InputTextBox}" Text="{Binding Value.Text}" />
<TextBox x:Name="TextBoxValue" Style="{StaticResource ReadOnlyTextBox}" />
<Button x:Name="ButtonRemove" Style="{StaticResource BasicButtonStyle}" Content="-" Click="ButtonRemove_Click" />
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
<ListBox.ItemContainerStyle>
<Style TargetType="ListBoxItem">
<Setter Property="HorizontalAlignment" Value="Left" />
<Setter Property="Background" Value="Yellow" />
</Style>
</ListBox.ItemContainerStyle>
<ListBox.Background>
<SolidColorBrush Color="Red" />
</ListBox.Background>
</ListBox>
<Button x:Name="ButtonAddCharaDisplayObject" Style="{StaticResource BasicButtonStyle}" Content="+" HorizontalAlignment="Right" Click="ButtonAddCharaDisplayObject_Click" />
</StackPanel>
I have no idea why the listbox doesn't shrink when the size of the items shrink, although I have set the listbox' size to Auto and HorizontalAlignment to Left
Thanks in advance,
Frank
I finally found the solution in this post. The problem is, that from Silverlight 3 on, the ListBox uses VirtualizationStackPanel to display the ListItems. Other than StackPanel, VirtualizationStackPanel uses all the space it gets and never gives it back. So, when the biggest item in your list shrinks and therefor the ListBox itself could shrink because now there is unused space, the ListBox' width (and height for that matter) will still stay the same because of VirtualizationStackPanel doesn't shrink properly.
To fix this, we can force the ListBox to use StackPanel instead of VirtualizationStackPanel. Note, that this may come at the cost of performance!
<ListBox HorizontalContentAlignment="Left" FontSize="9.333" HorizontalAlignment="Left">
... // other listbox related stuff
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel />
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
</ListBox>
Well... I don't have all of your code. But, I simplified what you had above to this and it works.
I hope this will help you, in some way, figure out your problem. Once again, it could be the parent of this control causing the problems. It could also be one of your styles you are applying. Try stripping out EVERYTHING from your control that doesn't have to be there, then add it back slowly to find the culprit.
I created a new silverlight application, and this is literally the only thing in it. The listbox grows and shrinks as expected.
XAML:
<UserControl
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"
x:Class="Test.MainPage">
<Grid x:Name="LayoutRoot">
<StackPanel VerticalAlignment="Top" HorizontalAlignment="Left">
<StackPanel.Background>
<SolidColorBrush Color="Black"/>
</StackPanel.Background>
<ListBox x:Name="ListBox" BorderBrush="{x:Null}" Margin="0" HorizontalContentAlignment="Left" FontSize="9.333" HorizontalAlignment="Left">
<ListBox.Foreground>
<SolidColorBrush Color="Silver"/>
</ListBox.Foreground>
<!-- DataTemplate to display the content -->
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanelOrientation="Horizontal" HorizontalAlignment="Left">
<TextBox FontSize="30" Text="{Binding}" />
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
<ListBox.ItemContainerStyle>
<Style TargetType="ListBoxItem">
<Setter Property="HorizontalAlignment" Value="Left" />
<Setter Property="Background" Value="Yellow" />
</Style>
</ListBox.ItemContainerStyle>
<ListBox.Background>
<SolidColorBrush Color="Red" />
</ListBox.Background>
</ListBox>
<StackPanel Orientation="Horizontal" HorizontalAlignment="Right" Height="30">
<Button Content="Add" Click="Add_Click" Width="100"/>
<Button Content="Remove" Click="Remove_Click" Width="100"/>
</StackPanel>
</StackPanel>
</Grid>
</UserControl>
Code Behind:
using System;
using System.Windows;
using System.Windows.Controls;
namespace Test
{
public partial class MainPage : UserControl
{
public MainPage()
{
// Required to initialize variables
InitializeComponent();
Count = 8;
}
private int Count;
private void Add_Click(object sender, System.Windows.RoutedEventArgs e)
{
Count = Count * 8;
ListBox.Items.Add("Hi Mom (" + Count.ToString() + ")");
}
private void Remove_Click(object sender, System.Windows.RoutedEventArgs e)
{
ListBox.Items.RemoveAt(ListBox.Items.Count-1);
}
}
}

Resources