Listview binding doesn't update the view - c#

I have this setup for my ListView and I am trying to make Binding to work in my MVVM structure.
This is the xaml code:
<Window.Resources>
<CollectionViewSource
x:Key="DeviceList"
Source="{Binding Path=DiscoveredDevicesList}">
</CollectionViewSource>
</Window.Resources>
<ListView
Grid.Row="1"
Width="500"
HorizontalAlignment="Left"
Margin="10"
Grid.Column="1"
DataContext="{StaticResource DeviceList}"
ItemsSource="{Binding}">
<ListView.View>
<GridView>
<GridViewColumn Header="Device name" DisplayMemberBinding="{Binding Path=DeviceName}"/>
<GridViewColumn Header="Rssi" DisplayMemberBinding="{Binding Path=Rssi}"/>
<GridViewColumn Header="GPS Row" DisplayMemberBinding="{Binding Path=GpsSignal}"/>
</GridView>
</ListView.View>
</ListView>
And the following is the code to implement the binding
//Constructor
public MainWindowViewModel()
{
DiscoveredDevicesList = new ObservableCollection<MyDeviceInfo>();
}
private ObservableCollection<MyDeviceInfo> _DiscoveredDevicesList;
public ObservableCollection<MyDeviceInfo> DiscoveredDevicesList
{
get
{
return _DiscoveredDevicesList;
}
set
{
_DiscoveredDevicesList = value;
OnPropertyChanged("DiscoveredDevicesList");
}
}
Using Add() and Clear() updates the view just fine when used as below:
var client = new BluetoothClient();
DiscoveredDevicesList.Clear();
IEnumerable<MyDeviceInfo> csaDevices = null;
csaDevices = await DiscoverCsaDevicesAsync(client);
foreach (var csaDevice in csaDevices)
{
DiscoveredDevicesList.Add(csaDevice);
DiscoveredDevicesList.First().GpsSignal = true;
}
So I can see the value of GpsSignal as changed to ture from initial opposite in my view.
However, if I put the following same line in the OnClick of a button doesn't do the same and the value stays false since I am not using any Add() or Clear() and I simply rely on the OnPropertyChanged to do the trick for me.
DiscoveredDevicesList.First().GpsSignal = true;
The rest of the bindings such as button clicks and textblock information work fine, then I would assume it shouldn't be the problem with implementation of the binding on the back of the stage.
Would appreciate your suggestions on this.

if your MyDeviceInfo class implement INotifyPropertyChanged and your property GpsSignal raise OnPropertyChanged() you should see all changes when your bindings are right.
nevertheless i would change your code in this way:
remove CollectionViewSource from your resources and use simple binding
<Window.Resources>
</Window.Resources>
<ListView ItemsSource="{Binding DeviceList}">
<ListView.View>
<GridView>
<GridViewColumn Header="Device name" DisplayMemberBinding="{Binding Path=DeviceName}"/>
<GridViewColumn Header="Rssi" DisplayMemberBinding="{Binding Path=Rssi}"/>
<GridViewColumn Header="GPS Row" DisplayMemberBinding="{Binding Path=GpsSignal}"/>
</GridView>
</ListView.View>
</ListView>
set the datacontext for your window to your MainWindowViewModel in codebehind or xaml.
instead of using OnClick, pls use ICommands (RelayCommand or DelegateCommand)
<Button Command="{Binding MyCommand4GpsSignalIsTrue}" />

Related

Bind Listview Items to Infobox

How can I bind the listview Item Properties of a selected item?
At the left side I show my files with sites in the listview and at the right i want to show the name, number of sites etc in a kind of Infobox (Grid with labels).
So how can I bind the list[index].name to the name label when i select it in the listview?
Or should I work with Selection Changed Event?
You Could bind the SelectedItem property of the GridView to your property on the ViewModel:
<DataGrid SelectedItem="{Binding SelectedItem, Mode=TwoWay}" >
.....
</DataGrid>
and in SelectedItem property:
private YourItemType_selectedItem;
public YourItemType SelectedItem
{
get { return _selectedItem; }
set
{
if(value != _selectedItem)
{
_selectedItem = value;
NotifyOfPropertyChange("SelectedItem");
// notify InfoBox property changed
NotifyOfPropertyChange("InfoBoxItem");
}
}
}
You could bind the TextBlocks/TextBoxes in the InfoBox to the SelectedItem property of the ListView.
Give the ListView an x:Name:
<ListView x:Name="listView">
<ListView.View>
<GridView>
<GridViewColumn Width="160" DisplayMemberBinding="{Binding Pos}" Header="Pos." />
<GridViewColumn Width="160" DisplayMemberBinding="{Binding Name}" Header="Name" />
<GridViewColumn Width="160" DisplayMemberBinding="{Binding Seitenzahl}" Header="Seitenzahl" />
</GridView>
</ListView.View>
</ListView>
...and use use an ElementName binding:
<TextBlock Text="{Binding SelectedItem.Name, ElementName=listView}" />
<TextBlock Text="{Binding SelectedItem.Pos, ElementName=listView}" />
<TextBlock Text="{Binding SelectedItem.Seitenzahl, ElementName=listView}" />
"Name", "Pos" and "Seitenzahl" are the name of the same properties that you bind to and display in the columns of the ListView.

How to resize ListView/GridView to contents in WPF

I would like to resize columns of a grid view into ListView to contents. There is the XAML code:
<ListView x:Name="listViewStatEntries"
AlternationCount="2" Grid.Row="8" Grid.ColumnSpan="3" Background="White"
ItemsSource="{Binding StatResult.StatEntries}" >
<ListView.View>
<GridView>
<GridViewColumn Width="Auto" DisplayMemberBinding="{Binding Param.Speciality}">
<GridViewColumn.Header>
<GridViewColumnHeader Content="{Binding Labels[2851]}" Tag="Param.Speciality" Click="listViewStatEntriesColumnHeader_Click" Width="Auto" />
</GridViewColumn.Header>
</GridViewColumn>
<GridViewColumn Width="Auto" DisplayMemberBinding="{Binding Alarm}">
<GridViewColumn.Header>
<GridViewColumnHeader Content="{Binding Labels[2765]}" Tag="Alarm" Click="listViewStatEntriesColumnHeader_Click" Width="Auto" />
</GridViewColumn.Header>
</GridViewColumn>
</GridView>
</ListView.View>
</ListView>
I found this thread: Force resize of GridView columns inside ListView and try Oskar solution:
public void AutoSizeColumns()
{
GridView gv = listView1.View as GridView;
if (gv != null)
{
foreach (var c in gv.Columns)
{
// Code below was found in GridViewColumnHeader.OnGripperDoubleClicked() event handler (using Reflector)
// i.e. it is the same code that is executed when the gripper is double clicked
if (double.IsNaN(c.Width))
{
c.Width = c.ActualWidth;
}
c.Width = double.NaN;
}
}
}
But with this solution, columns was resized to visible contents only and not to all data into the ListView. How can I resize column to all data?
Thanks in advance for your help !
The reason they resize only to the visible rows is because you have virtualization on (which is default). Try turning it off
<ListView VirtualizingStackPanel.IsVirtualizing="False"

Listview in Usercontrol in Flyout remains empty

I'm trying to place a usercontrol in a flyout window. In this usercontrol I'm trying to bind a list to a listview, but the listview remains empty.
C# UserControl
public MusicList()
{
InitializeComponent();
}
public void Makemusiclist(Inhabitant inhabitant, Wait wait, Rectangle rect)
{
musicList.Width = rect.Width;
musicList.Height = rect.Height;
lViewMusicFiles.Width = rect.Width - 150; // place the box on the right plce on any screen
lViewMusicFiles.Height = rect.Height - 50;
List<MusicFile> playlist = new List<MusicFile>(); // get the inhabitants' playlist from his file
playlist = inhabitant.musicList;
lViewMusicFiles.DataContext = playlist;
lViewMusicFiles.ItemsSource = playlist;
lViewMusicFiles.Items.Refresh();
musicList.UpdateLayout();
Dispatcher.BeginInvoke(DispatcherPriority.Loaded, new Action(() =>
{
wait.Close();
}));
}
XAML Usercontrol
<Grid>
<ListView x:Name="lViewMusicFiles" HorizontalAlignment="Left" VerticalAlignment="Top" Background="Red" Foreground="White" FontSize="32" Margin="150,50,0,0" ScrollViewer.VerticalScrollBarVisibility="Auto" SelectionMode="Single" SelectionChanged="lViewMusicFiles_SelectionChanged" Style="{DynamicResource ListViewStyle1}" >
<ListView.View>
<GridView>
<GridViewColumn Header="Titel" Width="Auto" DisplayMemberBinding="{Binding Path=title}"/>
<GridViewColumn Header="Artist" Width="Auto" DisplayMemberBinding="{Binding Path=artist}"/>
<GridViewColumn Header="Album" Width="Auto" DisplayMemberBinding="{Binding Path=album}"/>
</GridView>
</ListView.View>
</ListView>
</Grid>
</UserControl>
XAML flyout
<Grid>
<local:MusicList x:Name="musicList" />
</Grid>
Use ObservableCollection<T> instead of List<T>. you can write binding in XAML like this. I didn't write irrelevant properties.
<ListView x:Name="lViewMusicFiles" ItemsSource="{Binding Playlist, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}}}">
<ListView.View>
<GridView>
<GridViewColumn Header="Titel" DisplayMemberBinding="{Binding title}"/>
<GridViewColumn Header="Artist" DisplayMemberBinding="{Binding artist}"/>
<GridViewColumn Header="Album" DisplayMemberBinding="{Binding album}"/>
</GridView>
</ListView.View>
</ListView>
Also you need this property.
public ObservableCollection<MusicFile> Playlist{ get; } = new ObservableCollection<MusicFile>();

Edit TextView column on ListView WPF

I'm Using WPF.
I have ListView with TextBox column and two checkboxs columns.
I want to edit the TextBox text by double click or something else.
What is the simple way to do that?
...
<GridViewColumn Header="Name" DisplayMemberBinding={Binding Path=fullName}" Width=500>
<GridViewColumn.CellTemplate>
<DataTemplate>
<TextBox Name="txtName"/>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
...
Here is a sample way of doing this...
public partial class MainWindow : Window
{
public List<string> Items { get; set; }
public MainWindow()
{
InitializeComponent();
Items = new List<string>();
LoadItems();
DataContext = this;
}
private void txtName_MouseDoubleClick(object sender, MouseButtonEventArgs e)
{
TextBox currentTextBox = (TextBox)sender;
if (currentTextBox.IsReadOnly)
currentTextBox.IsReadOnly = false;
else
currentTextBox.IsReadOnly = true;
}
private void LoadItems()
{
Items.Add("Coffee");
Items.Add("Sugar");
Items.Add("Cream");
}
}
<Grid>
<ListView ItemsSource="{Binding Items}">
<ListView.View>
<GridView>
<GridViewColumn Header="Name">
<GridViewColumn.CellTemplate>
<DataTemplate>
<TextBox Name="txtName" Text="{Binding Mode=OneTime}" IsReadOnly="True" MouseDoubleClick="txtName_MouseDoubleClick" Width="100"/>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
</GridView>
</ListView.View>
</ListView>
</Grid>
Here is an example I have from an application that I wrote. The column JobName was to be user editable. This example allows the column to be editable and also gets rid of the border and lets the background blend into the row so it doesn't appear to have a text box in it.
Those can be edited out (BorderThickness="0" Background="Transparent").
My example binds to an MVVM ViewModel property called JobName and is set to be "TwoWay" so that changes to the view model will also reflect on the UI.
<ListView x:Name="lvJobs" HorizontalAlignment="Left" Height="628" Margin="30,62,0,0" ItemsSource="{Binding Jobs}"
SelectedItem="{Binding SelectedJob, Mode=TwoWay}" VerticalAlignment="Top" Width="335">
<ListView.View>
<GridView>
<GridViewColumn Header="Active" Width="50">
<GridViewColumn.CellTemplate>
<DataTemplate>
<CheckBox IsChecked="{Binding IsActive, Mode=TwoWay}"/>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
<GridViewColumn Header="Job Name" Width="150">
<GridViewColumn.CellTemplate>
<DataTemplate>
<TextBox Text="{Binding JobName, Mode=TwoWay}" BorderThickness="0" Background="Transparent"/>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
<GridViewColumn DisplayMemberBinding="{Binding User}" Header="User" Width="125"/>
</GridView>
</ListView.View>
</ListView>

WPF - Filling a ListView

I have a 2 column ListView and I'm trying to fill it using and IDictionary with the following code.
System.Collections.IDictionary entryList = Environment.GetEnvironmentVariables(EnvironmentVariableTarget.User);
foreach (System.Collections.DictionaryEntry de in entryList)
{
Row row = new Row();
row.Name1 = (string)de.Key;
row.Name2 = (string)de.Value;
this.list1.Items.Add(row);
}
public class Row
{
public string Name1 { get; set; }
public string Name2 { get; set; }
}
XAML:
<ListView x:Name="varList"
Grid.ColumnSpan="1"
HorizontalAlignment="Left"
VerticalAlignment="Top"
Height="400"
Width="500"
Margin="0, 30, 0, 0">
<ListView.View>
<GridView>
<GridViewColumn Width="150" Header="Name" />
<GridViewColumn Width="350" Header="Path" />
</GridView>
</ListView.View>
</ListView>
But every row and column gets filled with "Project.Views.Row".
Anyone got any idea on how to fix it? Thank you very much.
A ListView (and every other control for that matter) will display the results of calling ToString when given an object to display.
For a standard class, thats its qualified name; Project.Views.Row in your example.
There are two ways to fix this:
Don't add an object. Instead, format the string as you want it ie:
list1.Items.Add(String.Format({0}:{1}, row.Name1, row.Name2));
Do this the right way and use MVVM. In this case your XAML needs a data template:
<ListView ItemsSource="{Binding Rows}">
<ListView.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Name1}"/>
<TextBlock Text="{Binding Name2}"/>
</StackPanel>
</DataTemplate>
<ListView.ItemTemplate>
</ListView>
For a grid view:
<ListView.View>
<GridView>
<GridViewColumn Header="Name" DisplayMemberBinding="{Binding Name1}" />
<GridViewColumn Header="Path" DisplayMemberBinding="{Binding Name2}"/>
</GridView>
</ListView.View>
Binding the ItemsSource is not actually necessary, but since we are doing everything the right way, you should do it so you are not directly manipulating the UI from code.

Categories