WPF ListView: Aligning text in selected columns - c#

<ListView ItemsSource="{Binding MyData}">
<ListView.View>
<GridView>
<GridViewColumn Header="col1" DisplayMemberBinding="{Binding Path=value1}">
<GridViewColumn.CellTemplate>
<DataTemplate>
<TextBlock TextAlignment="Right" Text="{Binding Path=value1}"/>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
<GridViewColumn Header="col2">
<GridViewColumn.CellTemplate>
<DataTemplate>
<TextBlock TextAlignment="Center" Text="{Binding Path=value2}"/>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
<GridViewColumn Header="col3" DisplayMemberBinding="{Binding Path=value3}"/>
</GridView>
</ListView.View>
<ListView ItemsSource="{Binding MyData}">
col1 is supposed to be right-aligned. (Not working)
col2 is supposed to be center-aligned. (Working)
col3 is supposed to be left-aligned. (Working)
Is there a reason DisplayMemberBinding is overriding CellTemplate? If so, is there a fix for this (while still using DisplayMemberBinding)?
Edit: I ended up implementing it like this:
<Window xmlns:util="clr-namespace:TestProject.Util">
<Window.Resources>
<Style TargetType="ListViewItem">
<Setter Property="HorizontalContentAlignment" Value="Stretch"/>
</Style>
<Style TargetType="GridViewColumnHeader">
<Setter Property="HorizontalContentAlignment" Value="Left"/>
</Style>
<DataTemplate x:Key="value1Template">
<TextBlock TextAlignment="Right" Text="{Binding Path=value1}"/>
</DataTemplate>
<DataTemplate x:Key="value2Template">
<TextBlock TextAlignment="Right" Text="{Binding Path=value2}"/>
</DataTemplate>
</Window.Resources>
<Grid>
<ListView ItemsSource="{Binding MyData}" IsSynchronizedWithCurrentItem="True" util:GridViewSort.Command="{Binding SortCommand}">
<ListView.View>
<GridView>
<GridViewColumn Header="col1" CellTemplate="{StaticResource value1Template}" util:GridViewSort.PropertyName="value1"/>
<GridViewColumn Header="col2" CellTemplate="{StaticResource value2Template}" util:GridViewSort.PropertyName="value2"/>
</GridView>
</ListView.View>
</ListView>
</Grid>
</Window>
In the code behind:
private RelayCommand sortCommand;
public ICommand SortCommand { get { return sortCommand ?? (sortCommand = new RelayCommand(Sort)); } }
private void Sort(object param)
{
var propertyName = param as string;
var view = CollectionViewSource.GetDefaultView(MyData);
var direction = ListSortDirection.Ascending;
if (view.SortDescriptions.Count > 0)
{
var currentSort = view.SortDescriptions[0];
if (currentSort.PropertyName == propertyName)
direction = currentSort.Direction == ListSortDirection.Ascending ? ListSortDirection.Descending : ListSortDirection.Ascending;
view.SortDescriptions.Clear();
}
if (!string.IsNullOrEmpty(propertyName))
view.SortDescriptions.Add(new SortDescription(propertyName, direction));
}

DisplayMemberBinding has the highest priority. You can not use it combined with CellTemplate. See here in the remarks section.
If you want to right-or center-align the content, you must declare the CellTemplate with the binding (as you did) and remove the DisplayMemberBinding-attribute. If you also want to change the column header alignment, you must also set the GridViewColumn.Header-property.

Just add the following after your Window tag:
<Window.Resources>
<Style TargetType="ListViewItem">
<Setter Property="HorizontalContentAlignment" Value="Right" />
</Style>
</Window.Resources>

Related

WPF ListView not caching images

I have a WPF application that uses a ListView with a grid that displays images directly from the web. When the list is populated the images load as expected, but as I scroll down (the list contains around 200 items on average) it starts reusing the items that aren't in view (as it should). However, this causes the images to be released from memory and as a result they get reloaded all over again when the user scrolls back up.
MainWindow.xaml
<ListView Grid.Row="3" ItemsSource="{Binding SearchResults}" Background="{StaticResource PrimaryBackground}" Foreground="{StaticResource PrimaryForeground}"
ui:GridViewSort.AutoSort="True" ui:GridViewSort.ShowSortGlyph="False" IsSynchronizedWithCurrentItem="True" VirtualizingStackPanel.IsVirtualizing="False">
<ListView.View>
<GridView>
<GridViewColumn Width="80">
<GridViewColumn.CellTemplate>
<DataTemplate DataType="{x:Type Foo}">
<Image>
<Image.Source>
<BitmapImage CacheOption="OnDemand" UriSource="{Binding PreviewImageUrl}" />
</Image.Source>
</Image>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
<GridViewColumn Header="Title" DisplayMemberBinding="{Binding Title}" ui:GridViewSort.PropertyName="Title" />
<GridViewColumn Header="Remix" DisplayMemberBinding="{Binding Remix}" ui:GridViewSort.PropertyName="Remix" />
<GridViewColumn Header="Artist" DisplayMemberBinding="{Binding Artist}" ui:GridViewSort.PropertyName="Artist" />
<GridViewColumn Header="Duration" DisplayMemberBinding="{Binding Duration}" ui:GridViewSort.PropertyName="Duration" />
<GridViewColumn Header="BPM" DisplayMemberBinding="{Binding Bpm}" ui:GridViewSort.PropertyName="Bpm" />
<GridViewColumn Header="Year" DisplayMemberBinding="{Binding Date}" ui:GridViewSort.PropertyName="Date" />
<GridViewColumn>
<GridViewColumn.CellTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<StackPanel.Resources>
<Style TargetType="Button">
<Setter Property="Margin" Value="0,0,10,0" />
</Style>
</StackPanel.Resources>
<Button Command="{Binding ElementName=Window, Path=DataContext.Download}" CommandParameter="{Binding}">Download</Button>
<Button Command="{Binding ElementName=Window, Path=DataContext.CopyLink}" CommandParameter="{Binding}">Copy link</Button>
</StackPanel>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
</GridView>
</ListView.View>
</ListView>
Setting the Image.Source property with a different CacheOption makes no difference. You can also see I disabled virtualization which is bad but it's the only way to have it keep the images in memory. Is there an easy way to stop this from happening while also enabling virtualization?
Add a readonly PreviewImage property to your search results item class that creates and holds the BitmapImage when it is first accessed:
public class SearchResult : INotifyPropertyChanged
{
private Uri previewImageUrl;
public Uri PreviewImageUrl
{
get { return previewImageUrl; }
set
{
previewImageUrl = value;
previewImage = null;
NotifyPropertyChanged(nameof(PreviewImageUrl));
NotifyPropertyChanged(nameof(PreviewImage));
}
}
private ImageSource previewImage;
public ImageSource PreviewImage
{
get
{
if (previewImage == null && previewImageUrl != null)
{
previewImage = new BitmapImage(previewImageUrl);
}
return previewImage;
}
}
...
}
and bind to it like this:
<GridViewColumn.CellTemplate>
<DataTemplate>
<Image Source="{Binding PreviewImage}"/>
</DataTemplate>
</GridViewColumn.CellTemplate>
Try this:
<Image
HorizontalOptions="CenterAndExpand"
VerticalOptions ="CenterAndExpand">
<Image.Source>
<UriImageSource Uri="{Binding Image}"
CacheValidity="14"
CachingEnabled="true"/>
</Image.Source>
</Image>

ListViewItem tooltip WPF

What I need is that when mouse per listviewitem show me all data from each in a tooltip.
This is a part of my viewmdel
...
...
using GalaSoft.MvvmLight;
using GalaSoft.MvvmLight.CommandWpf;
...
...
private ObservableCollection<Articulo> _articulos;
private Articulo _articuloSeleccionado;
public ObservableCollection<Articulo> Articulos
{
get { return _articulos; }
set
{
_articulos = value;
RaisePropertyChanged();
}
}
public Articulo ArticuloSeleccionado
{
get { return _articuloSeleccionado; }
set
{
_articuloSeleccionado = value;
RaisePropertyChanged();
}
}
My .xalm
<ListView Name="lvResultado"
ItemsSource="{Binding Articulos}"
SelectedItem="{Binding ArticuloSeleccionado}">
<ListView.ItemContainerStyle>
<Style TargetType="ListViewItem">
<Setter Property="HorizontalContentAlignment" Value="Center"/>
</Style>
</ListView.ItemContainerStyle>
<ListView.View>
<GridView>
<GridViewColumn Header="Código de barras" Width="200" DisplayMemberBinding="{Binding CodigoDeBarras}"/>
<GridViewColumn Header="Descripción" Width="250" DisplayMemberBinding="{Binding Descripcion}"/>
</GridView>
</ListView.View>
</ListView>
Thank you for your help. I tried several things but no good result.
You can define an ItemContainerStyle that would set your tooltip template and content.
See an example below, here I define an UniformGrid to display multiple text lines in one column. You are free to setup your tooltip as you wish. You still need to tell the view which data properties need to be displayed in the tooltip.
<ListView ItemsSource="{Binding Articulos}">
<ListView.ItemContainerStyle>
<Style TargetType="{x:Type ListViewItem}">
<Setter Property="ToolTip">
<Setter.Value>
<UniformGrid Columns="1">
<TextBlock Text="{Binding CodigoDeBarras}"/>
<TextBlock Text="{Binding Descripcion}"/>
<TextBlock Text="{Binding AnyOtherProperty}"/>
</UniformGrid>
</Setter.Value>
</Setter>
</Style>
</ListView.ItemContainerStyle>
</ListView>

Checkbox ListView WPF C#

I will start developing for WPF and I have a doubt.
I created a ListView with the Binding property to the next ExtComandaDTO the object. The property in "seleciona" has relationship with a checkbox, but I have following problem.
When I click the checkbox it calls the normal event, but when I change the value of "seleciona" at runtime checkbox in my listview is selected but does not call the event check.
There is missing from Listview to be called the event some attribute?
<ListView x:Name="LvwComanda" Grid.Column="0"
Background="{x:Null}"
Margin="40,36,40,40"
SelectedItem="{Binding SelectedExtComanda}"
ItemsSource="{Binding ObsExtComanda, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
Grid.RowSpan="2" >
<ListView.ContextMenu>
<ContextMenu>
<MenuItem Header="Finaliza Comanda" Checked="LvwComandaRowFinalizaComanda_Click" Unchecked="LvwComandaRowFinalizaComanda_Click"></MenuItem>
</ContextMenu>
</ListView.ContextMenu>
<ListView.Resources>
<Style TargetType="{x:Type ListViewItem}">
<Style.Triggers>
<DataTrigger Binding="{Binding finaliza_pendente}" Value="true">
<Setter Property="Foreground" Value="Red" />
</DataTrigger>
<DataTrigger Binding="{Binding finalizada}" Value="true">
<Setter Property="Foreground" Value="DarkViolet" />
</DataTrigger>
</Style.Triggers>
</Style>
</ListView.Resources>
<ListView.View>
<GridView >
<GridViewColumn Width="30">
<GridViewColumn.CellTemplate>
<DataTemplate >
<CheckBox Name="ChkComanda" IsChecked="{Binding seleciona.IsChecked, Mode=TwoWay}" Checked="Checked_LvwComandaRow" Unchecked="Unchecked_LvwComandaRow" />
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
<GridViewColumn Width="Auto" Header="Comanda" DisplayMemberBinding="{Binding nr_comanda}"/>
<GridViewColumn Width="Auto" Header="Taxa Servico" DisplayMemberBinding="{Binding taxa_servico}" />
<GridViewColumn Width="Auto" Header="Finalizada" DisplayMemberBinding="{Binding finalizada, Converter={StaticResource ReplaceConvertSimNao}}" />
<GridViewColumn Width="Auto" Header="Observacao" DisplayMemberBinding="{Binding observacao}"/>
</GridView>
</ListView.View>
<ListView.GroupStyle>
<GroupStyle>
<GroupStyle.ContainerStyle>
<Style TargetType="{x:Type GroupItem}">
<Setter Property="Margin" Value="0,0,0,0"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type GroupItem}">
<Expander IsExpanded="True" BorderBrush="#FFA4B97F" BorderThickness="0,0,0,1">
<Expander.Header>
<DockPanel>
<DockPanel.ContextMenu>
<ContextMenu Loaded="LvwComandaHeaderContextMenu_Loaded">
<MenuItem Header="Libera Mesa" Checked="LvwComandaHeaderLiberaMesa_Click" Unchecked="LvwComandaHeaderLiberaMesa_Click" />
</ContextMenu>
</DockPanel.ContextMenu>
<CheckBox x:Name="HeaderCheckBox" Checked="Checked_LvwComandaHeader" Unchecked="Unchecked_LvwComandaHeader">
<StackPanel Orientation="Horizontal">
<TextBlock FontWeight="Bold" Text="{Binding Name, Converter={StaticResource ReplaceConvertMesaId}}" Margin="5,0,0,0"/>
<TextBlock Width="Auto" Text=" " />
<TextBlock FontWeight="Bold" Width="Auto" Text="{Binding Name, Converter={StaticResource ReplaceConvertMesaGrupo}}" />
<TextBlock Text=" ("/>
<TextBlock Text="{Binding ItemCount, Converter={StaticResource ReplaceConvertComanda}}"/>
<TextBlock Text=")"/>
</StackPanel>
</CheckBox>
</DockPanel>
</Expander.Header>
<ItemsPresenter />
</Expander>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</GroupStyle.ContainerStyle>
</GroupStyle>
</ListView.GroupStyle>
</ListView>
#region Event List Row Comanda
private void Checked_LvwComandaRow(object sender, RoutedEventArgs e)
{
this.Handle_LvwComandaRow((CheckBox)sender, true);
}
private void Unchecked_LvwComandaRow(object sender, RoutedEventArgs e)
{
this.Handle_LvwComandaRow((CheckBox)sender, false);
}
private void Handle_LvwComandaRow(CheckBox sender, bool check)
{
if (sender.DataContext is ExtComandaDTO)
{
var row = (ExtComandaDTO)sender.DataContext;
if (check)
{
ObsExtComanda.FindAll(c => c.seleciona && c.id_mesa != row.id_mesa).ForEach(c => c.seleciona = false);
}
bool bolComandaSelected = ObsExtComanda.Exists(c => c.seleciona);
BtPagamento.IsEnabled = bolComandaSelected;
BtImprimir.IsEnabled = bolComandaSelected;
this.PrepareObsPedido(check, row);
this.PrepareObsComandaPagto(check, row);
}
}
public class ExtComandaDTO : ComandaDTO, INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(String property)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(property));
}
}
private Boolean _seleciona;
private Boolean _finaliza_pendente;
public Boolean seleciona
{
get { return _seleciona; }
set { _seleciona = value; OnPropertyChanged("seleciona"); }
}
public new Boolean finaliza_pendente
{
get { return _finaliza_pendente; }
set { _finaliza_pendente = value; OnPropertyChanged("finaliza_pendente"); }
}
}
Checked and Unchecked are events fired by the UI (not a set).
Handle that stuff in the set if you want to catch changes from code.

How to retrieve the first value in a multi-column listview

Here it the listview element in XAML
<ListView x:Name="jobsListView_manageajob" HorizontalAlignment="Left" Height="488" Margin="39,163,0,0" VerticalAlignment="Top" Width="215" FontSize="15" BorderThickness="0" SelectionChanged="jobsListView_manageajob_SelectionChanged">
<ListView.Resources>
<Style TargetType="GridViewColumnHeader">
<Setter Property="Visibility" Value="Collapsed" />
</Style>
</ListView.Resources>
<ListView.View>
<GridView AllowsColumnReorder="False">
<GridViewColumn DisplayMemberBinding="{Binding JobNumberListView1}" Width="50"/>
<GridViewColumn DisplayMemberBinding="{Binding JobNameListView1}"/>
</GridView>
</ListView.View>
</ListView>
I need to retrieve the currently selected item from only one row
private void jobsListView_manageajob_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
searchTextBox_manageajob.Text = Convert.ToString(jobsListView_manageajob.SelectedItem);
}
Any help is really appreciated, I am so stuck!
Try to cast selected item object to the actual model type, then get it's property of interest :
var myModel = (ClassName)jobsListView_manageajob.SelectedItem;
searchTextBox_manageajob.Text = myModel.JobNameListView1;

How to verify a string changed from a textbox in listview is not the same as any exiting ones

I have a ListView box containing TextBoxes that allow users to add and change the content. How do I verify that the content that is changed is not the same as any exiting one in C# behind?
Xaml:
<ListView
x:Name="_regionQueryListBox"
Width="122"
HorizontalAlignment="Left"
VerticalAlignment="Stretch"
DataContext="{Binding}"
IsSynchronizedWithCurrentItem="True"
Style="{StaticResource ListViewRegionSelectorStyle}"
ItemsSource="{Binding Path=Model}"
ScrollViewer.HorizontalScrollBarVisibility="Disabled"
SelectionChanged="_regionQueryListBox_SelectionChanged">
<ListView.ItemContainerStyle>
<Style TargetType="ListViewItem">
<Setter Property="HorizontalContentAlignment" Value="Stretch" />
</Style>
</ListView.ItemContainerStyle>
<ListView.View>
<GridView>
<GridViewColumn Header="Region" Width="{Binding Path=ActualWidth, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=ListView}}">
<GridViewColumn.CellTemplate>
<DataTemplate>
<TextBox
HorizontalAlignment="Left"
VerticalAlignment="Stretch"
MaxLength="16"
Width="110"
Margin="-2,0,0,0"
Padding="-2,0,0,0"
Text="{Binding Path=RegionName}"/>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
</GridView>
</ListView.View>
</ListView>
Yes, it is MVVM. I have a validation for adding same item and you can find the Model like below:
private void OnQueryCollectionChanged(object sender, NotifyCollectionChangedEventArgs args)
{
if (Model.Count == 0)
{
CurrentRegionViewModel = null;
}
if (args.Action == NotifyCollectionChangedAction.Add)
{
RegionQuery addedRegionQuery = args.NewItems.OfType<RegionQuery>().FirstOrDefault();
if (addedRegionQuery != null)
{
string name = addedRegionQuery.RegionName;
while (Model.Any(q => q.RegionName == name && q != addedRegionQuery))
{
name += "*";
}
addedRegionQuery.RegionName = name;
}
}

Categories