WPF: how to auto update ObservableCollection inside ListView - wpf

I have my object collection:
public class Data
{
string name {get; set;}
int progres {get; set;}
}
public ObservableCollection<Data> dataFiles { get; set; }
And my ListView:
<ListView Name="lvDataFiles"
ItemsSource="{Binding dataList}">
<ListView.ItemContainerStyle>
<Style TargetType="{x:Type ListViewItem}">
<Setter Property="Foreground" Value="White"/>
</Style>
</ListView.ItemContainerStyle>
<ListView.Resources>
<DataTemplate x:Key="MyDataTemplate">
<Grid Margin="-6">
<ProgressBar Name="prog" Maximum="100" Value="{Binding Progress}"
Width="{Binding Path=Width, ElementName=ProgressCell}"
Height="16" Margin="0" Foreground="#FF5591E8" Background="#FF878889" />
<TextBlock Text="{Binding Path=Value, ElementName=prog, StringFormat={}{0}%}" VerticalAlignment="Center"
HorizontalAlignment="Center" FontSize="11" Foreground="White" />
</Grid>
</DataTemplate>
<ControlTemplate x:Key="ProgressBarTemplate">
<Label HorizontalAlignment="Center" VerticalAlignment="Center" />
</ControlTemplate>
</ListView.Resources>
<ListView.View>
<GridView ColumnHeaderContainerStyle="{StaticResource ListViewHeaderStyle}">
<GridViewColumn Width="425" Header="File name" DisplayMemberBinding="{Binding FileName}" />
<GridViewColumn x:Name="ProgressCell" Width="50" Header="Progress"
CellTemplate="{StaticResource MyDataTemplate}" />
</GridView>
</ListView.View>
</ListView>
My ListView has 2 columns: file name and Progress (that contain progress bar)
My Data collection has the Progress property that changing every few seconds.
Is it possible that my ListView ProgressBar will update automatic each time specific object (or several in the same time..) changing ?
Or i need to go over my collection and update ?

Your Data class must inherit from INotifyPropertyChanged, add a NotifyPropertyChange method and call that for each setter.
public class Data : INotifyPropertyChanged
{
private string _name;
public string name
{
get { return _name; }
set
{
_name= value;
NotifyPropertyChanged("name");
}
}
private int _progress;
public int progress
{
get { return _progress; }
set
{
_progress = value;
NotifyPropertyChanged("progress");
}
}
public event PropertyChangedEventHandler PropertyChanged;
virtual public void NotifyPropertyChange( string propertyName )
{
var handler = PropertyChanged;
if (handler != null)
handler(this, new PropertyChangedEventArgs(propertyName));
}
}

Related

Image icon and string do not appear in Listview in WPF

this is XAMl code
Image Icon and string don't appear in ListView in WPF
What's wrong?
Not only the image, but also other text strings do not appear. Trying other methods says that the ItemSource is overlap.
Is the binding not being applied?
<GroupBox Grid.Column="1" Grid.RowSpan="2" Background="Aqua">
<StackPanel Orientation="Vertical" Background="Aquamarine" Margin="5">
<StackPanel.Resources>
<Style x:Key="ListviewStyle" TargetType="{x:Type ListView}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ListView}">
<Border x:Name="corner"
CornerRadius="9"
Background="White"
Height="1000">
<ContentPresenter
VerticalAlignment="Center"
HorizontalAlignment="Center"
/>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</StackPanel.Resources>
<TextBlock Text="I/O Input"
FontSize="30"
VerticalAlignment="Center"
HorizontalAlignment="Center"
Margin="10"/>
<ListView Style="{StaticResource ListviewStyle}"
x:Name="lv_input">
<ListView.View>
<GridView>
<GridViewColumn Width="50" Header="InputStatus">
<GridViewColumn.CellTemplate>
<DataTemplate>
<Image Source="{Binding InputStatus}"/>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
<GridViewColumn Width="200" Header="InputName"
DisplayMemberBinding="{Binding InputName}"/>
</GridView>
</ListView.View>
</ListView>
</StackPanel>
</GroupBox>
this is behind code
Image Icon and string don't appear in ListView in WPF
What's wrong?
Not only the image, but also other text strings do not appear. Trying other methods says that the ItemSource is overlap.
Is the binding not being applied?
enter code here
public partial class IOSetPage : Page
{
public ObservableCollection<ListViewItemsData> ListViewItemsCollections { get { return ListViewItemsCollections; } }
ObservableCollection<ListViewItemsData> _ListViewItemsCollections = new ObservableCollection<ListViewItemsData>();
public IOSetPage()
{
InitializeComponent();
ListViewItemsCollections.Add(new ListViewItemsData()
{
InputStatus = "/Image/inputdisconnect.png",
InputName = "ASFWQEFAasdfqerwefc"
});
lv_input.ItemsSource = ListViewItemsCollections;
}
public class ListViewItemsData
{
public string InputStatus { get; set; }
public string InputName { get; set; }
}
}
Considering the example you've provided, it appears you have a typo on the 3rd line of your C# code. The property getter should be returning _ListViewItemsCollections like this:
public ObservableCollection<ListViewItemsData> ListViewItemsCollections { get { return _ListViewItemsCollections; } }
Also, I noticed that you are using ItemsSource directly, rather than setting the DataContext on the lv_input ListView. Whilst ItemsSource may work after fixing the typo, you may encounter unexpected behavior when trying to use item templates etc...
A more correct (but not perfect) way of doing what you are attempting would look like this:
public partial class IOSetPage : Page
{
IOSetPageView view;
public IOSetPage()
{
InitializeComponent();
view = new IOSetPageView();
view.Items.Add(new ListViewItemsData()
{
InputStatus = "/Image/inputdisconnect.png",
InputName = "ASFWQEFAasdfqerwefc"
});
lv_input.DataContext = view;
}
}
public class IOSetPageView
{
public ObservableCollection<ListViewItemsData> Items { get; }
= new ObservableCollection<ListViewItemsData>();
}
public class ListViewItemsData
{
public string InputStatus { get; set; }
public string InputName { get; set; }
}
With the XAML for the ListView looking like this:
<ListView Style="{StaticResource ListviewStyle}" ItemsSource="{Binding Items}" x:Name="lv_input">
<ListView.View>
<GridView>
<GridViewColumn Width="50" Header="InputStatus">
<GridViewColumn.CellTemplate>
<DataTemplate>
<Image Source="{Binding InputStatus}"/>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
<GridViewColumn Width="200" Header="InputName"
DisplayMemberBinding="{Binding InputName}"/>
</GridView>
</ListView.View>
</ListView>
Notice how the ItemSource is now bound to the Items property of the view class I created. To improve further, you could implement the INotifyPropertyChanged interface for your ListViewItemsData class which would allow the ListView to update in real time when you modify a property.

Data in TreeView.ItemTemplate HierarchicalDataTemplate gets erased when switching the Selected TreeItem

I have the following code:
Template.XAML
<Style TargetType="{x:Type HeaderedContentControl}">
<Setter Property="Header">
<Setter.Value>
<ContentControl Foreground="Red"
FontFamily="Segoe UI"
Margin="0,0,0,20"
Content="{Binding Tag, RelativeSource={RelativeSource AncestorType=HeaderedContentControl}}"
HorizontalAlignment="Center"
VerticalAlignment="Center" />
</Setter.Value>
</Setter>
</Style>
<DataTemplate DataType="{x:Type local:ViewModel}">
<HeaderedContentControl xmlns:sys="clr-namespace:System;assembly=mscorlib"
Tag="{Binding Header}"
Background="SteelBlue"
BorderBrush="DarkSlateBlue">
</HeaderedContentControl>
</DataTemplate>
<DataTemplate DataType="{x:Type local:ViewModel2}">
<HeaderedContentControl xmlns:sys="clr-namespace:System;assembly=mscorlib"
Tag="{Binding Header}"
Background="SteelBlue"
BorderBrush="DarkSlateBlue">
</HeaderedContentControl>
</DataTemplate>
Windows.XAML:
<Window.DataContext>
<local:WindowsVM x:Name="viewModel"/>
</Window.DataContext>
<Window.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="Templates.xaml" />
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Window.Resources>
<StackPanel Orientation="Horizontal">
<TreeView SelectedItemChanged="TreeView_SelectedItemChanged" ItemsSource="{Binding AllContents}">
<TreeView.ItemTemplate>
<HierarchicalDataTemplate>
<Label Content="{Binding Header}" />
</HierarchicalDataTemplate>
</TreeView.ItemTemplate>
</TreeView>
<StackPanel Orientation="Vertical">
<ContentControl Content="{Binding SelectedItem}" />
</StackPanel>
</StackPanel>
Windows.XAML.cs
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void TreeView_SelectedItemChanged(object sender, RoutedPropertyChangedEventArgs<object> e)
{
viewModel.SelectedItem = (ViewModel)e.NewValue;
}
}
ViewModel.cs
public class WindowsVM : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public WindowsVM()
{
AllContents = new ObservableCollection<ViewModel>();
AllContents.Add(new ViewModel("Item 1"));
AllContents.Add(new ViewModel2("Item 2")); //using ViewModel("Item 2") will show the Header as it should
SelectedItem = AllContents.First();
}
private ViewModel _selectedItem;
public ViewModel SelectedItem
{
get { return _selectedItem; }
set
{
_selectedItem = value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(SelectedItem)));
}
}
private ObservableCollection<ViewModel> _allContents;
public ObservableCollection<ViewModel> AllContents
{
get { return _allContents; }
set
{
_allContents = value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(AllContents)));
}
}
}
public class ViewModel:INotifyPropertyChanged
{
private string _header;
public event PropertyChangedEventHandler PropertyChanged;
public string Header
{
get { return _header; }
set
{
_header = value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(Header)));
}
}
public ViewModel(string header)
{
Header = header;
}
}
public class ViewModel2 : ViewModel
{
public ViewModel2(string header) : base(header)
{
}
}
If you run the application, the "Item 1" will be shown as the header of the selected item, as it should.
If you click on the second tree node, I would expect "Item 2" shown as the header of the selected item, but it is not.
Here comes the interesting part, if for "Item 2", it is of the type ViewModel instead of ViewModel2, then the "Item 2" header will be shown. How come this is happening? Is this a WPF treeview bug?
I would also welcome workaround, in the spirit of MVVM model.
You should bind the Header property of the HeaderedContentControl to your Header source property and then define a HeaderTemplate in the HeaderedContentControl style.
If you implement Templates.xaml like this, your example works as expected:
<Style TargetType="{x:Type HeaderedContentControl}">
<Setter Property="HeaderTemplate">
<Setter.Value>
<DataTemplate>
<Label Foreground="Red"
FontFamily="Segoe UI"
Margin="0,0,0,20"
Content="{Binding}"
HorizontalAlignment="Center"
VerticalAlignment="Center" />
</DataTemplate>
</Setter.Value>
</Setter>
</Style>
<DataTemplate DataType="{x:Type local:ViewModel}">
<HeaderedContentControl Header="{Binding Header}"
Background="SteelBlue"
BorderBrush="DarkSlateBlue">
</HeaderedContentControl>
</DataTemplate>
<DataTemplate DataType="{x:Type local:ViewModel2}">
<HeaderedContentControl Header="{Binding Header}"
Background="SteelBlue"
BorderBrush="DarkSlateBlue">
</HeaderedContentControl>
</DataTemplate>

Get Values in TextBox inside ListView

I am created a ListView with TextBox Control.I need to get values in TextBox.
After user typed on Textbox.I need to get whats the user typed.
<Window x:Class="LdiaryEditableListView.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Ldiary Editable ListView Sample" Height="350" Width="300">
<StackPanel >
<ListView Name="listView">
<ListView.ItemContainerStyle>
<Style TargetType="{x:Type ListViewItem}">
<EventSetter Event="Button.Click" Handler="Button_Click"/>
<Style.Triggers>
<DataTrigger Binding="{Binding Path=FirstName}" Value="">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate>
<StackPanel Orientation="Horizontal">
<TextBox Width="100" Text="{Binding Path=FirstName}"/>
<TextBox Width="100" Text="{Binding Path=LastName}"/>
<Button Width="70" >Add</Button>
</StackPanel>
</ControlTemplate>
</Setter.Value>
</Setter>
</DataTrigger>
</Style.Triggers>
</Style>
</ListView.ItemContainerStyle>
<ListView.View>
<GridView>
<GridViewColumn Width="100" Header="First Name" DisplayMemberBinding="{Binding Path=FirstName}"></GridViewColumn>
<GridViewColumn Width="100" Header="Last Name" DisplayMemberBinding="{Binding Path=LastName}"></GridViewColumn>
</GridView>
</ListView.View>
</ListView>
</StackPanel>
Iam created a function to get each element in textbox in listview.But it dows not work
foreach(DataRowView itm in lstvQualification.Items)
{
MessageBox.Show(itm[0].ToString());
}
I got solution for above my problem.
using System.ComponentModel;
using System.Collections.ObjectModel;
namespace LdiaryEditableListView
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
ObservableCollection<Person> people;
public MainWindow()
{
InitializeComponent();
people = new ObservableCollection<Person>(){
new Person{FirstName="", LastName=""},
new Person{FirstName = "Ldiary", LastName="Translations"},
new Person{FirstName = "English", LastName="Japanese"}
};
listView.ItemsSource = people;
}
public class Person : INotifyPropertyChanged
{
private string _firstName;
public string FirstName
{
get
{
return _firstName;
}
set
{
_firstName = value;
}
}
private string _lastname;
public string LastName
{
get
{
return _lastname;
}
set
{
_lastname = value;
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
private void Button_Click(object sender, RoutedEventArgs e)
{
Person newPerson = people[0];
if (string.IsNullOrEmpty(newPerson.FirstName) || string.IsNullOrEmpty(newPerson.LastName))
{
newPerson.FirstName = "";
newPerson.LastName = "";
MessageBox.Show("Please provide both first name and last name!");
}
else
{
Person emptyPerson = new Person()
{
FirstName = "",
LastName = ""
};
people.Insert(0, emptyPerson);
}
listView.ItemsSource = null;
listView.ItemsSource = people;
}
}
}

WPF Listview Item Clicked Name

I have a listview with 5 columns. In the first column i insert an image in wich i have an event MouseClicked event. What i want to accomplish is when i click the image from row 5 , to get the name of item in row 5 - column 2;
This is my code so far :
<ListView x:Name="HistoryListB">
<ListView.ItemContainerStyle>
<Style TargetType="{x:Type ListViewItem}">
<Setter Property="Background" Value="#FF515050"/>
<Style.Triggers>
<Trigger Property="Control.IsMouseOver" Value="true">
<Setter Property="Background" Value="#FF515050"/>
<Setter Property="BorderBrush" Value="#FF3B3A3A"/>
</Trigger>
</Style.Triggers>
</Style>
</ListView.ItemContainerStyle>
<ListView.View>
<GridView ColumnHeaderContainerStyle="{StaticResource myHeaderStyle}">
<GridViewColumn Width="90" Header="Image" >
<GridViewColumn.CellTemplate>
<DataTemplate>
<Image Name="Favorite" Width="12" Height="12" Source="{Binding ImageSource}" MouseLeftButtonDown="Image_MouseLeftButtonDown" />
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
<GridViewColumn Width="90" Header="Time Added" DisplayMemberBinding="{Binding Time}" />
<GridViewColumn Width="110" Header="Status" DisplayMemberBinding="{Binding Status}"/>
<GridViewColumn Width="290" Header="ItemTitle" DisplayMemberBinding="{Binding ItemTitle}"/>
<GridViewColumn Width="50" Header="Duration" DisplayMemberBinding="{Binding Duration}"/>
</GridView>
</ListView.View>
</ListView>
And i add items in listview like this. I have a class and a list like this :
public class HistoryItems
{
public string Time { get; set; }
public string Status { get; set; }
public string ItemTitle { get; set; }
public string Duration { get; set; }
public ImageSource ImageSource { get; set; }
}
IList<HistoryItems> SHistoryItems { get; set; }
And then add the items :
SHistoryItems = new List<HistoryItems>() {
new HistoryItems () {
Time = DateTime.Now.ToString("HH:mm:ss tt"),
Status = "Started Playing : ",
ItemTitle = StationL.StationName
}
};
foreach (var item in SHistoryItems)
HistoryListB.Items.Add(SHistoryItems);
And in my code i want to get the name of the item from same row but 2nd column :
private void Image_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
TextBlock1.Text = ?? THE NAME OF THE SECOND COLUMN IN THE SAME ROW ?? ;
}
You can use the Tag property of the Image to hold the HistoryItem then you can access through the sender argument in the event handler.
Example:
Xaml:
<GridViewColumn.CellTemplate>
<DataTemplate>
<Image Name="Favorite" Width="12" Height="12" Source="{Binding ImageSource}" Tag="{Binding}" MouseLeftButtonDown="Image_MouseLeftButtonDown" />
</DataTemplate>
</GridViewColumn.CellTemplate>
Code:
private void Image_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
var image = sender as Image;
if (image != null && image.Tag is HistoryItems)
{
TextBlock1.Text = (image.Tag as HistoryItems).ItemTitle;
}
}

SelectedItem of ListBox with DataTemplate

I have a ListBox:
<ListBox Name="lbsfHolder"
ItemsSource="{Binding UISupportingFunctions}"
SelectedItem="{Binding Path=SelectedSupportedFunction, Mode=TwoWay}"
SelectionMode="Multiple"
IsSynchronizedWithCurrentItem="True"
HorizontalContentAlignment="Stretch">
<ListBox.ItemTemplate>
<DataTemplate>
<controls:SupportingFunction GotFocus="SupportingFunction_GotFocus"/>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
In the ViewModel I have:
private SupportingFunction _selectedSupportedFunction;
public SupportingFunction SelectedSupportedFunction
{
get { return _selectedSupportedFunction; }
set
{
_selectedSupportedFunction = value;
NotifyPropertyChanged("SelectedSupportedFunction");
}
}
But when I'm trying to select any item in list box nothing happens. The SelectedItem is null for the ListBox and for SelectedValue, too. Do I need to add some special code to make this work?
UPD:
I've changed views a bit, now I have:
<UserControl x:Class="RFM.UI.WPF.Controls.SupportingFunction">
<Grid>
<ListBox Name="supportingFunctions"
ItemsSource="{Binding UISupportingFunctions}"
SelectedItem="{Binding Path=SelectedSupportedFunction, Mode=TwoWay}"
SelectionMode="Multiple"
IsSynchronizedWithCurrentItem="True"
HorizontalContentAlignment="Stretch">
<ListBox.ItemContainerStyle>
<Style TargetType="{x:Type ListBoxItem}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="30" />
<ColumnDefinition />
<ColumnDefinition Width="30" />
</Grid.ColumnDefinitions>
<TextBox Name="tbsfName" Grid.Column="0" Text="{Binding Path=Title, Mode=TwoWay}"></TextBox>
<TextBox Name="tbsfExperssion" Grid.Column="1" Text="{Binding Path=Expression}" HorizontalAlignment="Stretch"></TextBox>
<Button Name="bsfDel" Grid.Column="2">Del</Button>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ListBox.ItemContainerStyle>
</ListBox>
</Grid>
</UserControl>
In Page where this control placed:
<StackPanel Name="spSupportingFunctions">
<StackPanel Name="spsfOperations" Orientation="Horizontal">
<Button Name="bsfAdd" Width="30" Command="commands:CustomCommands.AddSupportingFunction">Add</Button>
</StackPanel>
<controls:SupportingFunction DataContext="{Binding Self}" />
</StackPanel>
at code behind of this Page
public PlotDataPage()
{
DataContext = new PlotDataViewModel();
InitializeComponent();
}
and this is the full listing of PlotDataViewModel
public class UISupportingFunction : ISupportingFunction
{
public string Title { get; set; }
public string Expression { get; set; }
}
public class PlotDataViewModel : INotifyPropertyChanged
{
public PlotDataViewModel Self
{
get
{
return this;
}
}
private ObservableCollection<UISupportingFunction> _supportingFunctions;
public ObservableCollection<UISupportingFunction> UISupportingFunctions
{
get
{
return _supportingFunctions;
}
set
{
_supportingFunctions = value;
NotifyPropertyChanged("UISupportingFunctions");
}
}
private UISupportingFunction _selectedSupportedFunction;
public UISupportingFunction SelectedSupportedFunction
{
get
{
return _selectedSupportedFunction;
}
set
{
_selectedSupportedFunction = value;
NotifyPropertyChanged("SelectedSupportedFunction");
}
}
public PlotDataViewModel()
{
UISupportingFunctions = new ObservableCollection<UISupportingFunction>();
}
public void CreateNewSupportingFunction()
{
UISupportingFunctions.Add(new UISupportingFunction() { Title = Utils.GetNextFunctionName() });
}
private void NotifyPropertyChanged(string info)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(info));
}
public event PropertyChangedEventHandler PropertyChanged;
}
I'm just calling the CreateNewSupportingFunction() method when I click Add button. Everything looks fine - the items is add and I see them. But when I'm clicking on one of the TextBoxes and then to the bsfDel button right to each item I'm getting null in SelectedSupportedFunction.
Maybe it is because of focus event have been handling by TextBox and not by ListBox?
It's either your ItemsSource UISupportingFunctions is not a SupportingFunction object or you did not set the View's Datacontext to your ViewModel.
ViewModel.xaml.cs
this.DataContext = new ViewModelClass();

Resources