Grouping ListBox with Date - c#

I have no idea how to achieve this, but I have a date and time column in a ListBox. The Date column should not display if the date was already in there. I know that in combination with ListCollectionView and Listview/DataGrids, it is propably possible. But can I achieve this with a ListBox and a List. Keep in mind I am using the MVVM principle. This is my listbox:
<ListBox Grid.Row="2" ScrollViewer.HorizontalScrollBarVisibility="Disabled" ItemsSource="{Binding Schedules}">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Background="Transparent">
<Grid Background="Transparent">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="100"/>
<ColumnDefinition Width="100"/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition/>
</Grid.RowDefinitions>
<TextBlock Grid.Column="0" Grid.Row="0" Text="{Binding MyDateTime, StringFormat='d' }" HorizontalAlignment="Left" VerticalAlignment="Center"/>
<TextBlock Grid.Column="1" Grid.Row="0" Text="{Binding MyDateTime, StringFormat='t'}" HorizontalAlignment="Left" VerticalAlignment="Center"/>
<TextBlock Grid.Column="2" Grid.Row="0" Text="{Binding SomeText}" TextTrimming="WordEllipsis" LineStackingStrategy="MaxHeight" MaxHeight="20" HorizontalAlignment="Left" VerticalAlignment="Center"/>
</Grid>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
I have made this in excel to give an example of what I am trying to achieve:
I want the affect where I have highlighted with yellow

Converters
// Order Schedules using System.Linq
public class ToOrderedListConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
List<ScheduleItem> schedules = (List<ScheduleItem>)value;
var subset = from item in schedules
orderby item.MyDateTime.TimeOfDay
orderby item.MyDateTime.ToString("yyyy/MM/dd") descending
select item;
return subset.ToList();
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
// Show only first occurrence of date
public class DateToVisibilityConverter : IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
DateTime currentItem = (DateTime)values[0];
List<ScheduleItem> schedules = (List<ScheduleItem>)values[1];
ScheduleItem firstOccurrence =
schedules.Find(item => item.MyDateTime.Year == currentItem.Year
&& item.MyDateTime.Month == currentItem.Month
&& item.MyDateTime.Day == currentItem.Day);
if (firstOccurrence.MyDateTime == currentItem)
return Visibility.Visible;
else return Visibility.Collapsed;
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
XAML
<ListBox Grid.Row="2" ScrollViewer.HorizontalScrollBarVisibility="Disabled"
ItemsSource="{Binding Schedules, Converter={StaticResource ToOrderedListConverter}}">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Background="Transparent">
<Grid Background="Transparent">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="100"/>
<ColumnDefinition Width="100"/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition/>
</Grid.RowDefinitions>
<TextBlock Grid.Column="0" Grid.Row="0" Text="{Binding MyDateTime, StringFormat='dd/MM/yyyy'}" HorizontalAlignment="Left" VerticalAlignment="Center">
<TextBlock.Visibility>
<MultiBinding Converter="{StaticResource DateToVisibilityConverter}">
<Binding Path="MyDateTime"/>
<Binding RelativeSource="{RelativeSource AncestorType={x:Type ListBox}}"
Path="ItemsSource"/>
</MultiBinding>
</TextBlock.Visibility>
</TextBlock>
<TextBlock Grid.Column="1" Grid.Row="0" Text="{Binding MyDateTime, StringFormat='t'}" HorizontalAlignment="Left" VerticalAlignment="Center"/>
<TextBlock Grid.Column="2" Grid.Row="0" Text="{Binding SomeText}" TextTrimming="WordEllipsis" LineStackingStrategy="MaxHeight" MaxHeight="20" HorizontalAlignment="Left" VerticalAlignment="Center"/>
</Grid>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>

You can use this approach for your requirements.
https://code.msdn.microsoft.com/windowsdesktop/CollectionView-Tips-MVVM-d6ebb4a7#content

Related

How to align labels differently in listview

I have 2 labels in a ListView.
1st label is an ItemName (with vertical align to start),
and the 2nd label is ItemDescription (with vertical align to end).
What I'm trying to achieve is...
When ItemDescription is empty, I want the ItemName to be vertical align to center
Since I'm new, it will be great if you can also show an example.
This is my Xaml (ItemsPage)
<ContentPage.Content>
<StackLayout>
<ListView ItemsSource="{Binding _items, Mode=TwoWay}" x:Name="lstView" SelectedItem="{Binding SelectedItem}">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<Grid Padding="0,0,8,0" Margin="3,0,3,0">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="7*"/>
</Grid.ColumnDefinitions>
<Image Source="{Binding ImageUrl}" Grid.Column="0" Margin="3"></Image>
<Label Text="{Binding ItemName}" MaxLines="1" LineBreakMode="TailTruncation" FontSize="Medium" FontAttributes="Bold"></Label>
<Label Text="{Binding ItemDescription}" MaxLines="1" LineBreakMode="TailTruncation" Grid.Column="1" VerticalTextAlignment="End"></Label>
</Grid>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</StackLayout>
</ContentPage.Content>
You can use the IValueConverter
https://learn.microsoft.com/en-us/xamarin/xamarin-forms/app-fundamentals/data-binding/converters
<Label Text="{Binding ItemName}" MaxLines="1" LineBreakMode="TailTruncation" FontSize="Medium" FontAttributes="Bold" VerticalTextAlignment="{Binding ItemDescription,Converter={StaticResource checkItemDescription}}"></Label>
Converter
public class AlignmentConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter,
CultureInfo culture)
{
if(value==null)
return LayoutOptions.Start;
return LayoutOptions.End;
}
public object ConvertBack(object value, Type targetType, object parameter,
CultureInfo culture)
{
throw new NotImplementedException();
}
}

Using MVVM instead of MultiValueConverter in WPF XAML

I've been working with XAML for some time now, but when I started I didn't knew much about MVVM so I avoided it. So all the logic in my projects is written in .xaml.cs instead of ViewModel.cs. Now the project is big and I have to move some parts of it to MVVM per request and I have no clue how to do so or where to actually start.
about the problem:
I have two Entities. lets call them A and Secret.
A can have Secret. If A does have it I need to display Secret.Name and Secret.Description beside the A data.
They are binded together with: A.Secret == Secret.Id.
A data and B data can change independently and is received/changed over API calls, but A.Secret and Secret.Id bind will never change.
Secrets is just an array of SecretModel elements
<ListBox ItemsSource="{Binding A, ElementName=uc}" SelectedItem="{Binding SelectedA, ElementName=uc}">
<ListBox.ItemTemplate>
<DataTemplate>
<Grid VerticalAlignment="Center">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto" MaxHeight="300"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<TextBlock Text="ID:" TextAlignment="Right" />
<TextBlock Grid.Column="1" Text="{Binding Id}"/>
<TextBlock Grid.Row="1" Text="Type:" TextAlignment="Right"/>
<TextBlock Grid.Row="1" Grid.Column="1" Text="{Binding Type}" />
<TextBlock Grid.Row="2" Text="Secret:" TextAlignment="Right" Visibility="{Binding Secret, Converter={StaticResource StringToVisibilityConverter}}"/>
<TextBlock Grid.Row="2" Grid.Column="1" Visibility="{Binding Secret, Converter={StaticResource StringToVisibilityConverter}}">
<TextBlock.Text>
<MultiBinding Converter="{StaticResource SecretIdToNameConverter}">
<Binding Path="Secret"/>
<Binding Path="Secrets" ElementName="uc"/>
</MultiBinding>
</TextBlock.Text>
<TextBlock.ToolTip>
<MultiBinding Converter="{StaticResource SecretIdToDescriptionConverter}">
<Binding Path="Secret"/>
<Binding Path="Secrets" ElementName="uc" />
</MultiBinding>
</TextBlock.ToolTip>
</TextBlock>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
Converters looks like this.
public class SecretIdToDescriptionConverter : IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
if (values[0] != null && values[0] != DependencyProperty.UnsetValue && values[0] is string SecretId && values[1] != null && values[1] != DependencyProperty.UnsetValue && values[1] is SecretModel[] Secrets)
{
var tmp = Secrets.FirstOrDefault(secret => secret?.Id == SecretId);
if (tmp != null)
return tmp.Description;
return null;
}
else
{
return null;
}
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
If anyone could give me some help/pointers on how I should/could migrate from Converters of this type to MVVM or point me to some resource explaining it to me - that would be greatly appreciated.
The things that mostly boggles me:
How should I share/acces the data betwean .xaml.cs or .xaml and ViewModel.cs at this point?
How are calls made to retrieve/give such data?
Is it possible to create method that would return the corresponding SecretModel, so I could just use it after the call in .xaml?
like
<magic ItemsSource= getSecret(A.Secret) >
<TextBlock Text = {Binding Name}/>
</magic>

wpf binding max width proportional to other element's width

I have the following ItemTemplate:
<ListView.ItemTemplate>
<DataTemplate>
<Border CornerRadius="5" Background="{Binding SenderId, Converter={StaticResource ColorConverter}}"
MaxWidth="{Binding ActualWidth, ElementName=SideBar}"
HorizontalAlignment="{Binding SenderId, Converter={StaticResource AlignmentConverter}}">
<TextBlock Text="{Binding Message}" TextWrapping="Wrap" />
</Border>
</DataTemplate>
</ListView.ItemTemplate>
What I would like to do is to bind the Border elements MaxWidth property to 70% of the SideBar's Width but I can't seem to find a way to do this in xaml.
Is there a clean way to achieve this?
To expand on Rohit Vats comment:
Add this class to your solution:
public class ElementSizeConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
double percentage = double.Parse(parameter.ToString());
return double.Parse(value.ToString()) * percentage;
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
return null;
}
}
Then, in your XAML, declare the namespace of where this class is in the document header:
xmlns:ConverterNamespace="clr-namespace:ConverterNamespace"
Instantiate the converter in the resources:
<Window.Resources>
<ConverterNamespace:ElementSizeConverter x:Key="ElementSizeConverter"/>
</Window.Resources>
And then you can use the following binding:
Width="{Binding Width, ElementName=elementName, Converter={StaticResource ElementSizeConverter}, ConverterParameter=0.7}"
Note: I couldn't get ActualWidth to work, but play around with this and see if this works for you.
By nesting in a grid you could also get the percentages:
<ListView.ItemTemplate>
<DataTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="15*" />
<ColumnDefinition Width="70*" />
<ColumnDefinition Width="15*" />
</Grid.ColumnDefinitions>
<Border Grid.Column="1"
CornerRadius="5"
Background="{Binding SenderId, Converter={StaticResource ColorConverter}}"
HorizontalAlignment="{Binding SenderId, Converter={StaticResource AlignmentConverter}}">
<TextBlock Text="{Binding Message}" TextWrapping="Wrap" />
</Border>
</Grid>
</DataTemplate>
</ListView.ItemTemplate>

Combobox itemtemplate and string or comboboxitem

Need a small fix with comboboxitems in wpf. I want to show the comboboxitems or in the comboxbox (textblock) with an image beside. I couldn't get it done. Can someone help me with the fix please?
Thanks in advance
<ComboBox Name="avilibity" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Grid.Column="1">
<ComboBox.ItemTemplate>
<DataTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="25"/>
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Image Grid.Column="1" Source="/resources/icon.png" ></Image>
<TextBlock FontSize="14" Grid.Column="2" ></TextBlock>
</Grid>
</DataTemplate>
</ComboBox.ItemTemplate>
<sys:String>Available</sys:String>
<sys:String>Offline</sys:String>
<sys:String>Away</sys:String>
</ComboBox>
First Problem is that the Grid Column index is 0 based - means Image shold have Grid.Column = "0", TextBlock = "1".
Should it be allways the same image? Consider using of user-defined object as property with binding. Thus you can have individual icons.
May be one of the gurus here can provide XAML-only example... i can imagine only with code behind...
Here two possible solutions:
Code behind in C#
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.ComponentModel;
namespace ListImageWithText
{
/// <summary>
/// Interaktionslogik für MainWindow.xaml
/// </summary>
public partial class MainWindow : Window, INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private void OnNotifyPropertyChanged(String info)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(info));
}
}
private List<ImageWithText> _objectlist;
public List<ImageWithText> ObjectList
{
get
{
return _objectlist;
}
set
{
_objectlist = value;
OnNotifyPropertyChanged("ObjectList");
}
}
public MainWindow()
{
InitializeComponent();
ImageWithText iwt;
this._objectlist = new List<ImageWithText>();
iwt = new ImageWithText();
iwt.Image = "App.ico";
iwt.Text = "Away";
this._objectlist.Add(iwt);
iwt = new ImageWithText();
iwt.Image = "App.ico";
iwt.Text = "Available";
this._objectlist.Add(iwt);
iwt = new ImageWithText();
iwt.Image = "App.ico";
iwt.Text = "Offline";
this._objectlist.Add(iwt);
OnNotifyPropertyChanged("ObjectList");
}
}
public class ImageWithText
{
public string Image { get; set; }
public string Text { get; set; }
}
}
Define small class with two public properties, present list of those objects as property and use binding in XAML
<ComboBox Name="avilibity"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
Grid.Column="1"
ItemsSource="{Binding ObjectList, RelativeSource={RelativeSource Mode=FindAncestor,AncestorType=Window}}">
<ComboBox.ItemTemplate>
<DataTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="25"/>
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Image Grid.Column="0" Source="{Binding Image}" />
<TextBlock FontSize="14" Grid.Column="1" Text="{Binding Text}"/>
</Grid>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
The simplest way with XAML repetitions - is use of ComboBox Items, i do not preffer this path...
<ComboBox Name="avilibity"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
Grid.Column="1">
<ComboBoxItem>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="25"/>
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Image Grid.Column="0" Source="App.ico" ></Image>
<TextBlock FontSize="14" Grid.Column="1" >Available</TextBlock>
</Grid>
</ComboBoxItem>
<ComboBoxItem>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="25"/>
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Image Grid.Column="0" Source="App.ico" ></Image>
<TextBlock FontSize="14" Grid.Column="1" >Offline</TextBlock>
</Grid>
</ComboBoxItem>
<ComboBoxItem>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="25"/>
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Image Grid.Column="0" Source="App.ico" ></Image>
<TextBlock FontSize="14" Grid.Column="1" >Away</TextBlock>
</Grid>
</ComboBoxItem>
</ComboBox>
There are few mistakes in your code:
1. Row & Column are start with index 0;
2. TextBlock need to set the Text Property, so that it can display.
If you just want to display the same icon,
this code will gonna work(this can fix your perblom):
<ComboBox Name="avilibity" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Grid.Column="1">
<ComboBox.ItemTemplate>
<DataTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="25"/>
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Image Grid.Column="0" Source="/resources/icon.png" ></Image>
<TextBlock FontSize="14" Grid.Column="1" Text="{Binding}"></TextBlock>
</Grid>
</DataTemplate>
</ComboBox.ItemTemplate>
<sys:String>Available</sys:String>
<sys:String>Offline</sys:String>
<sys:String>Away</sys:String>
</ComboBox>
But as I saw, there are three kind of state of the items.
Maybe you want to change the image when different item display.
Here is my solution:
<Window.Resources>
<converters:ImageSourceConverter x:Key="ImageSourceConverter" />
</Window.Resources>
<ComboBox Name="avilibity" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Grid.Column="1">
<ComboBox.ItemTemplate>
<DataTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="25"/>
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Image Grid.Column="0" Source="{Binding Converter={StaticResource ImageSourceConverter}}" />
<TextBlock FontSize="14" Grid.Column="1" Text="{Binding}"></TextBlock>
</Grid>
</DataTemplate>
</ComboBox.ItemTemplate>
<sys:String>Available</sys:String>
<sys:String>Offline</sys:String>
<sys:String>Away</sys:String>
</ComboBox>
ImageSourceConverter:
public class ImageSourceConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
return new BitmapImage(new Uri("/resources/" + value + ".png",UriKind.RelativeOrAbsolute));
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}

Concatenate binding text and static text in source image

Is it possible to concatenate binding text with a static text in source image
For example:
<Image Name="ImagePlace" Source="http://site.com/image/architecture.png" Grid.Column="0" />
<Image Name="ImagePlace" Source="{Binding Path=Ico}" Grid.Column="0" />
And i would like to concatenate the two sources.
I have a list of object named Category that contains a field Icon that contains for example "architecture.png". I bind it in a list.
The url of site doesn't change but the image changes always.
<ListBox x:Name="ListboxCategories">
<ListBox.ItemTemplate>
<DataTemplate>
<Button Name="category" Tag="{Binding Path=Id}" Tap="category_Tap_1">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="2*"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Image Name="ImagePlace" Source="http://www.test.com/assets/images/icons/tags/architecture.png" Grid.Column="0" />
<TextBlock Text="{Binding Path=Title}" Grid.Column="1" HorizontalAlignment="Center" />
<Image Name="chevron" Grid.Column="2" Source="/Assets/AppBar/White/appbar.chevron.right.png" HorizontalAlignment="Right" Width="50" Height="50" />
</Grid>
</Button>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
The cleanest way (well, depending on your architecture) would be to add a property in your object to do the concatenation:
public string FullUri
{
get
{
return "http://site.com/image/" + this.Ico
}
}
Another way is to use a custom converter to do the concatenation.
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value == null)
{
return null;
}
return "http://site.com/image/" + value.ToString();
}

Categories