Unable to Bind ObservableCollection from MVVM to ListView in WPF - c#

I am trying to bind the List View to Observable Collection of a class and for some reason, the DisplayMemberBinding attribute of GridViewColumn is not binding to the content of collection.
If I use ListView.ItemTemplate, everything works fine. But I need the data in the form of a grid, so I am using GridView inside ListView.View.
First Image, Image 1 is the AssessmentSummaryList content in debug mode.
Second Image, Image 2 is the final output shown on the screen. The content from the list is not binding to the GridViewColumn.
These are the two view models I am using:
public class AssessmentSummaryViewModel : BaseViewModel
{
public string Question { get; set; }
public string Title { get; set; }
public string SelectedOption { get; set; }
public int Id { get; set; }
}
public class AssessmentViewModel : BaseViewModel
{
private ObservableCollection<AssessmentSummaryViewModel> assessmentSummaryList;
public ObservableCollection<AssessmentSummaryViewModel> AssessmentSummaryList
{
get { return assessmentSummaryList; }
set
{
assessmentSummaryList = value;
NotifyPropertyChanged("AssessmentSummaryList");
}
}
public void SetNextAssessment()
{
AssessmentSummaryList= Service.GetAssessmentSummary(ApplicationModel.SelectedModule.Id,
ApplicationModel.SelectedUtility.Id); //the service returns ObservableCollection<AssessmentSummaryViewModel> data
var EndScreen = new AssessmentSummaryView(); //Setting the last screen to AssessmentSumamryView, this will be called dynamically from other UserControl Xaml Page
}
}
AssessmentSummaryView Xaml Code is as follows:
<ListView x:Name="lstvSummary1" Grid.Row="2" ItemsSource="{Binding Path=DataContext.AssessmentSummaryList, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type UserControl}}}">
<ListView.View>
<GridView ColumnHeaderContainerStyle="{StaticResource myHeaderStyle}">
<GridViewColumn DisplayMemberBinding="{Binding Path=Title}" Width="200">
<GridViewColumnHeader Content="Design Element" />
</GridViewColumn>
<GridViewColumn DisplayMemberBinding="{Binding Path=Question}" Width="400">
<GridViewColumnHeader Content="Question" />
</GridViewColumn>
<GridViewColumn DisplayMemberBinding="{Binding Path=SelectedOption}" Width="300">
<GridViewColumnHeader Content="Response" />
</GridViewColumn>
</GridView>
</ListView.View>
</ListView>
AssessmentSummaryView Code-behind is as follows:
public partial class AssessmentSummaryView : UserControl
{
public AssessmentSummaryView()
{
InitializeComponent();
}
}

Try something like:
<ListView ItemsSource="{Binding Path=DataContext.AssessmentSummaryList, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type UserControl}}}">
<ListView.View>
<GridView>
<GridView.Columns>
<GridViewColumn Header="Design Element" Width="200" DisplayMemberBinding="{Binding Path=Title}"/>
<GridViewColumn Header="Question" Width="400" DisplayMemberBinding="{Binding Path=Question}"/>
<GridViewColumn Header="Response" Width="300" DisplayMemberBinding="{Binding Path=SelectedOption}"/>
</GridView.Columns>
</GridView>
</ListView.View>

Related

Problems with Binding Checkboxes in a Listview in xaml Code

What i have:
i have a ListView with 6 Values(string) and a Name(string). I also added a Checkbox for each row.
enter image description here
Struct:
public struct ststuff
{
public bool bEnable { get; set; }
public string sone { get; set; }
public string stwo { get; set; }
public string stree { get; set; }
public string sa { get; set; }
public string sb { get; set; }
public string sc { get; set; }
public string sName { get; set; }
}
Collection:
private ObservableCollection<ststuff> _astuff;
Some Code:
<ListView Margin="10,100,10,10" Name="lvstuff" ItemsSource="{Binding astuff,Mode=TwoWay,ElementName=window}" SelectedIndex="{Binding nIndex,Mode=TwoWay,ElementName=window}">
<ListView.View>
<GridView>
<GridViewColumn Header="[]">
<GridViewColumn.CellTemplate>
<DataTemplate>
<CheckBox HorizontalAlignment="Center" VerticalAlignment="Center" IsChecked="{Binding bEnable,ElementName=window}"/>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
<GridViewColumn Header="Name" Width="150" DisplayMemberBinding="{Binding sName}"/>
<GridViewColumn Header="a" Width="100" DisplayMemberBinding="{Binding sa}" />
<GridViewColumn Header="b" Width="100" DisplayMemberBinding="{Binding sb}" />
<GridViewColumn Header="c" Width="100" DisplayMemberBinding="{Binding sc}" />
<GridViewColumn Header="1" Width="100" DisplayMemberBinding="{Binding sone}" />
<GridViewColumn Header="2" Width="100" DisplayMemberBinding="{Binding stwo}" />
<GridViewColumn Header="3" Width="100" DisplayMemberBinding="{Binding stree}" />
</GridView>
</ListView.View>
</ListView>
Now what i want:
Click on one check box and save true/false in a a value.(e.g. _astuff[0].bEnable for first checkbox)
Atm. if i click on one box all boxes goes check/uncheck because i binded all to the same value.
Somehow i want to bind IsChecked Property of Checkbox to bEnable of my struct but i dont know how!
I got it maybe not the best way but it works.
private void CheckBox_Checked(object sender, RoutedEventArgs e)
{
CheckBox cb = e.OriginalSource as CheckBox;
int index = _astuff.IndexOf((ststuff)(cb).DataContext);
ststuff stuff= _astuff[index];
stuff.bEnable = true;
_astuff[index] = stuff;
}
The problem is that the selected index of ListView dont change by check/uncheck a checkbox. In this way you can find the index in your collection. You have to do the same with Uncheck event.

How to delete row in listview MVVM?

I have 1 list. Each row of the list will have a delete button. I want to delete that line when I click that button and must use mvvm.
<ListView x:Name="ListViewRoutePlan" SelectedIndex="0" ItemsSource="{Binding RoutePlanResource}" Style="{StaticResource MaterialDesignListView}">
<ListView.View>
<GridView>
<GridViewColumn DisplayMemberBinding="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type ListViewItem}},Converter={StaticResource IndexConverter}}" Header="STT" />
<GridViewColumn DisplayMemberBinding="{Binding Prioritize}" Header="Prioritize" />
<GridViewColumn DisplayMemberBinding="{Binding PlanStatus}" Header="Plan Status" />
<GridViewColumn DisplayMemberBinding="{Binding Note}" Header="Note" />
<GridViewColumn Header="Delete" >
<GridViewColumn.CellTemplate>
<DataTemplate>
<Button Command="{Binding ElementName=ListViewRoutePlan,Path=DataContext.RemoveSubjectCommand}" CommandParameter="{Binding ElementName=ListViewRoutePlan}" Background="{x:Null}" BorderBrush="{x:Null}" Margin="0,-5,0,0" HorizontalAlignment="Left">
<materialDesign:PackIcon Kind="Delete" Margin="-5,0,0,0" Foreground="Black" Width="20" Height="20" />
</Button>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
</GridView>
</ListView.View>
view model
I have run it here but I do not know how to write processing code to delete
public class viewmodel : BaseViewModel
{
public ICommand RemoveSubjectCommand { get; set; }
public viewmodel()
{
RemoveSubjectCommand = new RelayCommand<ListView>((p) => { return true; }, (OnEdit));
}
private void OnEdit(ListView lsv)
{
}
}
The button command parameter should be the current item bound to the cell
CommandParameter="{Binding}"
And the command should be updated accordingly to remove the selected item from the collection
public class viewmodel : BaseViewModel {
public viewmodel() {
RemoveSubjectCommand = new RelayCommand<MyItemModel>((p) => { return true; }, (OnRemoveSubject));
//assuming RoutePlanResource initialized and populated
}
public ObservableCollection<MyItemModel> RoutePlanResource {
//assuming boilerplate getter and setter with notification
}
public ICommand RemoveSubjectCommand { get; set; }
private void OnRemoveSubject(MyItemModel item) {
RoutePlanResource.Remove(item);
//...
}
}

ListView Grid in grid

I have a listview with id, name and price. Each such an item has a sublist with categories.
So "for each" Item I want to display all subitems.
This is would it should look like:
But I don't know how to do it.
Here is my xaml code.
<ListView.View>
<GridView>
<GridViewColumn Width="140" Header="ID" DisplayMemberBinding="{Binding ID}"/>
<GridViewColumn Width="140" Header="Name" DisplayMemberBinding="{Binding Name}" />
<GridViewColumn Width="300" Header="Price" DisplayMemberBinding="{Binding Price}" />
</GridView>
<!-- ??? -->
<Gridview ItemSource="{Binding Childs}">
I have to add a subgrid I think, but I don't know how.
This is my class structure
public class GroupedItem
{
public int ID { get; set; }
public string Name { get; set; }
public float Price { get; set; }
public IEnumerable<Item> Childs { get; set; }
}
Has someone an idea?
you can do this with datagrid :
<w:DataGrid ItemsSource={Binding Source={StaticResource GroupedItemList}}>
<w:DataGrid.RowDetailsTemplate>
<DataTemplate>
<w:DataGrid ItemsSource={Binding Childs }>
</w:DataGrid>
</DataTemplate>
</w:DataGrid.RowDetailsTemplate>
<w:DataGrid.Columns>
<w:DataGridTextColumn Header="Name" DisplayMemberBinding="{Binding Name}" />
<w:DataGridTextColumn Header="Price" DisplayMemberBinding="{Binding Price}" />
</w:DataGrid.Columns>
Also you can use this datagrid in codeplex it's free
WPF Extended DataGrid
You might want to use so-called tree-grid-view. There are really cool proprietary implementations by DevExpress and Infragistics. But also there are free soultions, descussed here
In your listviewitem you will have to build a template with an other listview
so your second listview is bind to
public IEnumerable<Item> Childs { get; set; }

Rules of binding an object in wpf

I am having a hard time binding a ListView with an ObservableCollection in another class.
My xaml:
<ListView Height="117" HorizontalAlignment="Left" Margin="20,239,0,0" Name="lvResults" VerticalAlignment="Top" Width="759" ItemsSource="{Binding RuleSearch.FileMatches}">
<ListView.View>
<GridView>
<GridViewColumn Header="Name" Width="120" DisplayMemberBinding="{Binding FileName}"/>
<GridViewColumn Header="Size" Width="120" DisplayMemberBinding="{Binding DirectoryName}"/>
<GridViewColumn Header="Date" Width="120" DisplayMemberBinding="{Binding Size}"/>
<GridViewColumn Header="Full Path" Width="120" />
<GridViewColumn Header="Some Meaningless Data" Width="120" />
</GridView>
</ListView.View>
</ListView>
The Xaml behind code:
private Search _ruleSearch = new Search();
public Search RuleSearch { get { return _ruleSearch; }}
In Search class:
public ObservableCollection<Result> FileMatches { get; private set; }
Note the changes are made on a new thread, if that makes a difference:
private void FindResultOnNewThreads()
{
FileMatches.Clear();
Parallel.ForEach(_fileList, file =>
{
foreach (Regex search in SearchTermList.Where(search => search.IsMatch(file)))
{
lock (FileMatches)
{
FileInfo fileInfo = new FileInfo(file);
FileMatches.Add(new Result
{
Attributes = fileInfo.Attributes,
DirectoryName = fileInfo.DirectoryName,
Extension = fileInfo.Extension,
FileName = fileInfo.Name,
FullNamePath = fileInfo.FullName,
Size = fileInfo.Length
});
}
}
});
}
Result class:
public class Result
{
public string FileName { get; set; }
public string DirectoryName { get; set; }
public string FullNamePath { get; set; }
public long Size { get; set; }
public string Extension { get; set; }
public FileAttributes Attributes { get; set; }
}
Issue really is, I am learning wpf by myself and couldn't really find a rule set for data binding in WPF. I know that it requires properties and public ones, other than that I am stuck.
Probably your listViews DataContext is not set to the UserControls or Windows or whatever, where you have your RuleSearch prop.
You can set it in the xaml.cs codebehind.
lvResult.DataContext = this;
or in xaml
<ListView Height="117" HorizontalAlignment="Left" Margin="20,239,0,0" Name="lvResults" VerticalAlignment="Top" Width="759" ItemsSource="{Binding Path=RuleSearch.FileMatches, RelativeSource={RelativeSource AncestorType={x:Type typeOfAncestor}}}">
<ListView.View>
<GridView>
<GridViewColumn Header="Name" Width="120" DisplayMemberBinding="{Binding FileName}"/>
<GridViewColumn Header="Size" Width="120" DisplayMemberBinding="{Binding DirectoryName}"/>
<GridViewColumn Header="Date" Width="120" DisplayMemberBinding="{Binding Size}"/>
<GridViewColumn Header="Full Path" Width="120" />
<GridViewColumn Header="Some Meaningless Data" Width="120" />
</GridView>
</ListView.View>
</ListView>
where typeOfAncestor is the type of your usercontrol/window...
Your Search class needs to implement INotifyPropertyChanged, which is used to let the UI know when the properties in your code-behind changes (in this case, FileMatches). This will involve registering a callback to the FileMatches.CollectionChanged event that then raises the INotifyPropertyChanged.PropertyChanged event with a property name of "FileMatches".
Note that if you ever want the values in the collection to stay in sync with the UI, you should also implement INotifyPropertyChanged on your Result class. (From looking at your code, the contents of your Result class looks constant, so you won't need to do this).

What's the quickest and easiest way to add items in a ListView that has multiple columns?

I have a ListView called lv with three columns. What's the quickest and easiest way to add items in it during runtime? I am using WPF.
Try this:
<ListView
x:Name="lv"
ItemsSource="{Binding Path=Items}"
SelectedItem="{Binding Path=SelectedItem}">
<ListView.View>
<GridView >
<GridViewColumn Header="Header1" DisplayMemberBinding="{Binding Path=Prop1}" />
<GridViewColumn Header="Header2" DisplayMemberBinding="{Binding Path=Prop2}"/>
<GridViewColumn Header="Header3" DisplayMemberBinding="{Binding Path=Prop3}"/>
</GridView>
</ListView.View>
</ListView>
In your ViewModel you should have some collection, like this:
public ObservableCollection<Test> Items { get; protected set; }
where Test is :
public class Test
{
public int Prop1{ get; set; }
public String Prop2{ get; set; }
public int Prop3{ get; set; }
}
When you will put/remove data in this "Items" Property, ListView will update itself automaticly.

Categories

Resources