How come my databinding is writing out the Length property? - c#

So I've setup a viewmodel to where it binds an ObservableCollection<string> to my DataGrid.
It prints out the value just fine but it also prints out the Length of the property? I don't recall ever setting that in any binding whatsoever. Why is it doing that?
My MainWindow.cs
public MainWindow()
{
InitializeComponent();
DataContext = new MasterViewModel();
}
MasterViewModel.cs
class MasterViewModel
{
public Users Users { get; } = new Users();
public Achievements Achievements { get; } = new Achievements();
}
Users.cs
class Users : INotifyPropertyChanged
{
public Users()
{
newList.Add("Hello there");
}
private ObservableCollection<string> newList = new ObservableCollection<string>();
public ObservableCollection<string> NewList
{
get { return newList; }
set { newList = value; }
}
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
XAML
<DataGrid ItemsSource="{Binding Users.NewList}" Width="400" Height="200" Margin="182,158,210,61">
<DataGrid.Columns>
<DataGridTemplateColumn>
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBlock Text="{Binding}"></TextBlock>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>

DataGrid has property AutoGenerateColumns which is set to True by default and makes DataGrid to create a column for each property defined in items.
DataGrid is bound to NewList which contains items of type string which has Length property. So it makes Length column
you can disable auto-generation by setting <DataGrid AutoGenerateColumns="False" ...

I forgot to add the property AutoGenerateColumns="False".
Not sure why it was set to true by default or why it woudl choose the length property of all the properties but I guess I got it fixed.

Related

WPF MVVM DataGrid ComboboxColumn binding to List in my Model

I am currently working a a WPF project with MVVM.
I have a DataGrid bound to an ObservableCollection of Models like this:
class Model : INotifyPropertyChanged
{
private string m_Name;
public string Name
{
get
{
return m_Name;
}
set
{
m_Name = value;
OnPropertyChanged("Name");
}
}
private List<string> m_Names;
public List<string> Names
{
get
{
return m_Names;
}
set
{
m_Names = value;
OnPropertyChanged("Names");
}
}
private double? m_Value;
public double? Value
{
get
{
return m_Value;
}
set
{
m_Value = value;
OnPropertyChanged("Value");
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
Now I'd like to use a DataGridComboBoxColumn to have a Combobox with my Property "Name" as the SelectedItem and Names as the ItemSource.
Each of my Models has it's own set of names, which aren't identical with the name of any of the other models.
I've googeled and looked through StackOverflow, but I didn't find any solution. I've also tried to aplly filters like i know DevExpress Grid Controls can do, but I didn't find anything for basic WPF DataGrids.
How can I bind my DataGridComboBoxColumn to a Property List in my Model?
If you use DataGridComboBoxColumn you have to feed ItemsSource with a static resource, this is stated in the "Remarks" section
https://learn.microsoft.com/en-us/dotnet/api/system.windows.controls.datagridcomboboxcolumn?view=netframework-4.8
Because you have different "Names" for each view model you can use DataGridTemplateColumn instead of DataGridComboBoxColumn
<DataGridTemplateColumn Header="Name">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<ComboBox ItemsSource="{Binding Names}">
<ComboBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding}"/>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
What did you try? Provided that the DataGrid's ItemsSource property is set or bound to an IEnumerable<Model>, this should work:
<DataGridComboBoxColumn ItemsSource="{Binding Names}" SelectedItemBinding="{Binding Name}" />
Please refer to this TechNet article for more suggestions.

WPF: Two way binding does not set source

My two way binding is just working from the source to the TextBox - I can see the default value in the TextBox and even the new value when I change it from the code-behind, but when I change the Text in the TextBox the value doesn't get updated in the Model, even after the TextBox loses focus. The DataContext is also set.
Version.Set doesn't even get called - tested by setting a breakpoint.
XAML:
<DataGrid ItemSource="{Binding Issues}">
<DataGrid.RowDetailsTemplate>
<TextBox Text="{Binding Path=TestReport.Version, Mode=TwoWay}"/>
</DataGrid.RowDetailsTemplate>
</DataGrid>
Models:
public class TestIssue
{
public JiraIssue Issue { get; set; }
public TestReport TestReport { get; set; }
}
public class TestReport : INotifyPropertyChanged
{
private string version = "Defalut Value";
public string Version
{
get => this.version;
set
{
if (value == this.version) return;
this.version = value;
this.OnPropertyChanged();
}
}
public event PropertyChangedEventHandler PropertyChanged;
[NotifyPropertyChangedInvocator]
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
Code Behind:
public partial class MainWindow : Window
{
public ObservableCollection<TestIssue> Issues { get; set; } = new ObservableCollection<TestIssue>();
public MainWindow()
{
this.DataContext = this;
this.InitializeComponent();
}
}
EDIT: Explicitly setting the UpdateSourceTrigger works, even setting it to FocusLost, which confuses me even more.
First, your XAML code is incorrect and it should be like this:
<DataGrid ItemsSource="{Binding Issues}">
<DataGrid.RowDetailsTemplate>
<ItemContainerTemplate >
<TextBox Text="{Binding Path=TestReport.Version, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>
</ItemContainerTemplate>
</DataGrid.RowDetailsTemplate>
</DataGrid>
You cannot place <TextBox/> element inside <DataGrid.RowDetailsTemplate> directlyand it should be placed inside <ItemContainerTemplate >.
To update the TextBox you need to tell TextBox element when it should update its value when the source changes by adding UpdateSourceTrigger=PropertyChanged to the binding script as shown in the above code.

Remove ListBoxItem from ViewModel

I develop CRUD app for WindowsPhone 8.1. I can add data to ObservableCollection collection and this data is displayed on ListBox. I use MVVM pattern.
Full repository https://github.com/OlegZarevych/CRUD_WP81
View :
<ListBox x:Name="Storage" ItemsSource="{Binding Path=Models, Mode=TwoWay}" >
<ListBox.ItemTemplate>
<DataTemplate>
<Grid Height="30" Width="450">
<TextBlock x:Name="nameblock" Text="{Binding Name}" />
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
And ViewModel class
public class ViewModel
{
public string NewName { get; set; }
public string NewSurname { get; set; }
public int NewAge { get; set; }
public int i=0 ;
public ObservableCollection<DataStorageModel> Models { get; set; }
//Event Handlers
public ICommand CreateClickCommand { get; set; }
public ICommand UpdateClickCommand { get; set; }
public ICommand DeleteClickCommand { get; set; }
public ViewModel()
{
CreateClickCommand = new RelayCommand(arg => CreateClickMethod());
UpdateClickCommand = new RelayCommand(arg => UpdateClickMethod());
DeleteClickCommand = new RelayCommand(arg => DeleteClickMethod());
Models = new ObservableCollection<DataStorageModel>() {};
}
private void CreateClickMethod()
{
Models.Add(new DataStorageModel() { Name = NewName, Surname = NewSurname, Age = NewAge, Count=i++ });
}
private void UpdateClickMethod()
{}
private void DeleteClickMethod()
{}
}
I want to change data and delete it. As i good understand, I need select count from ListBoxItems and delete(update) this count in ObservableCollection.
How can I work with XAML code from ViewModel class ?
How can I initiliaze Storage in ViewModel ?
Or in MVVM is the better way to resolve this problem ?
When you want to delete a model from the ListBox you typically need some way to identify the selected ListBoxItems (or models) that you want to delete; for that, consider having an IsSelected property on your models and bind it to a CheckBox inside the ListBoxItem data template.
Now, when you click on delete, the delete command can then easily look into the Models list and see which items are selected for deletion. After it deletes the items, it can then enumerate over the collection and recalculate the count value for the remaining items and update the field in the view model.
So, you don't have to access the XAML to update the count of the models. If you make the count property mutable then you wouldn't have to reinitialize the storage after you delete items from the list.
I added code t the Model
private bool _isSelected;
public bool IsSelected
{
get { return _isSelected; }
set
{
if (_isSelected != value)
{
_isSelected = value;
OnPropertyChanged("IsSelected");
}
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
Also added checkbox with bindin to View.
<ListBox x:Name="Storage" Background="Gray" FontSize="14" ItemsSource="{Binding Path=Models, Mode=TwoWay}" >
<ListBox.ItemTemplate>
<DataTemplate>
<Grid Height="60" Width="400" >
<CheckBox x:Name="checkbox" IsChecked="{Binding Path=IsSelected, UpdateSourceTrigger=PropertyChanged}" />
<TextBlock x:Name="nameblock" Text="{Binding Name}"/>
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
But IsSelected var doesn't change when I check checkbox in item
Why ?

Databind itemsource of DataGridComboBoxColumn to collection in view model does not work

What I am trying to do here is databind the Itemsource of a DataGridComboBoxColumn to a collection of strings declared as property of my item view model.
The Datagrid itself is bound to another viewmodel which has a collection of viewModels that represent the rows on the datagrid.
All my other bindings work properly. The collection is also filled, but the combobox remains empty.
XAML:
<Window.Resources>
<ResourceDictionary>
<local:GeneralDataGridViewModel x:Key="generalDataGridVm"/>
</ResourceDictionary>
</Window.Resources>
<Grid>
<DataGrid DataContext="{StaticResource generalDataGridVm}"
ItemsSource="{Binding Collection}">
<DataGrid.Columns>
<DataGridComboBoxColumn x:Name="chbCodes"
Header="Code"
ItemsSource="{Binding Path=DataContext.Collection, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}" />
</DataGrid.Columns>
</DataGrid>
</Grid>
C# ItemViewModel:
public class ItemViewModel : INotifyPropertyChanged
{
private ObservableCollection<string> _collection;
public ObservableCollection<string> Collection
{
get
{
return _collection;
}
}
public Model Model { get; set; }
public string Code
{
get { return Model.Code; }
set { Model.Code = value; }
}
public ItemViewModel()
{
}
public ItemViewModel(Model model)
{
Model = model;
_collection = new ObservableCollection<string>();
_collection.Add(model.Code);
Model.PropertyChanged += Model_PropertyChanged;
}
public void Model_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
}
}
c# DataGridViewModel:
public class GeneralDataGridViewModel
{
private ObservableCollection<ItemViewModel> _collection;
public ObservableCollection<ItemViewModel> Collection
{
get { return _collection; }
set
{
_collection = value;
NotifyPropertyChanged("Collection");
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected void NotifyPropertyChanged(String property)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(property));
}
}
public GeneralDataGridViewModel()
: base()
{
_collection = new ObservableCollection<ItemViewModel>();
}
public GeneralDataGridViewModel(List<Model> models)
{
_collection = new ObservableCollection<ItemViewModel>((from m in models
select new ItemViewModel(m)).ToList());
}
}
C# Model:
public class Model: INotifyPropertyChanged
{
public string Code { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public event PropertyChangedEventHandler PropertyChanged;
public void NotifyPropertyChanged(String property)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(property));
}
}
public override string ToString()
{
return Code;
}
}
The code you have posted does not compile, but looking at it the issue might be with the data context, which you are setting to a static resource. If you are going to do this, the view model must be in your resource dictionary. The bindings in XAML are fine, see below for an example of this working:
XAML:
<Window.Resources>
<ResourceDictionary>
<local:GeneralDataGridViewModel x:Key="generalDataGridVm"/>
</ResourceDictionary>
</Window.Resources>
<StackPanel Orientation="Vertical">
<DataGrid DataContext="{StaticResource generalDataGridVm}" Name="DataGrid1" ItemsSource="{Binding Collection}">
<DataGrid.Columns>
<DataGridComboBoxColumn x:Name="chbCodes"
Header="Code"
ItemsSource="{Binding Path=DataContext.Collection, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}" />
</DataGrid.Columns>
</DataGrid>
</StackPanel>
By declaring generalDataGridVm in XAML, the constructor is called in XAML, let's assume that construction supplies the values for the collection:
public GeneralDataGridViewModel() : base()
{
_collection = new ObservableCollection<ItemViewModel>();
_collection.Add(new ItemViewModel(new Model("code1")));
_collection.Add(new ItemViewModel(new Model("code2")));
_collection.Add(new ItemViewModel(new Model("code3")));
_collection.Add(new ItemViewModel(new Model("code4")));
}
With this code, it results in a populated list:
So I think you just need to make sure that you are declaring your view model properly (I would suggest not creating this in XAML unless there is some good reason).
Then ensure that the collection is kept up to date properly in that particular instance of your view model.

ListView not updated after adding new items to List

I have a ListView bounded to a List of a class I created. When doing an operating, it was supposed to add/remove items from the list, but my ListView wasn't updated even though I used INotifyPropertyChanged.
If I use ObservableCollection, it works but I need to have the list sorted, and ObservableCollection doesn't do sorting for WPF4.0 :(
Any way I can make the List binding work? Why didn't it work even though I used INotifyPropertyChanged?
XAML:
<ListView BorderThickness="0" ItemsSource="{Binding SelectedValues, UpdateSourceTrigger=PropertyChanged, Mode=OneWay}" Padding="5">
<ListView.View>
<GridView ColumnHeaderContainerStyle="{StaticResource myHeaderStyle}">
<GridViewColumn DisplayMemberBinding="{Binding Value}"></GridViewColumn>
VM:
private List<CheckBoxItem> _selectedValues = new List<CheckBoxItem>();
public List<CheckBoxItem> SelectedValues
{
get { return _selectedValues; }
set
{
_selectedValues = value;
OnPropertyChanged();
}
}
private void UnselectValueCommandExecute(CheckBoxItem value)
{
value.IsSelected = false;
SelectedValues.Remove(value);
//OnPropertyChanged("SelectedValues");
OnPropertyChanged("IsAllFilteredValuesSelected");
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
}
The CheckBoxItem class contains 2 properties, Value and IsChecked, which I don't think is relevant here.
So basically, I have a button which uses the UnselectValueCommandExecute to remove items from the list, and I should see the list updated in the UI, but I'm not.
When I debug, I can see the SelectedValues list updated, but not my UI.
You need a CollectionViewSource in your UI.
The XAML:
<Window x:Class="WavTest.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:scm="clr-namespace:System.ComponentModel;assembly=WindowsBase"
Title="MainWindow" Height="350" Width="525">
<Window.Resources>
<CollectionViewSource Source="{Binding TestSource}" x:Key="cvs">
<CollectionViewSource.SortDescriptions>
<scm:SortDescription PropertyName="Order"/>
</CollectionViewSource.SortDescriptions>
</CollectionViewSource>
</Window.Resources>
<ListView ItemsSource="{Binding Source={StaticResource cvs}}" DisplayMemberPath="Description"/>
</Window>
The code behind:
namespace WavTest
{
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
var vm = new ViewModel();
this.DataContext = vm;
vm.TestSource.Add(new TestItem { Description="Zero", Order=0 });
}
}
public class ViewModel
{
public ObservableCollection<TestItem> TestSource { get; set; }
public ViewModel()
{
TestSource = new ObservableCollection<TestItem>();
TestSource.Add(new TestItem { Description = "Second", Order = 2 });
TestSource.Add(new TestItem { Description = "Third", Order = 3 });
TestSource.Add(new TestItem { Description = "First", Order = 1 });
}
}
public class TestItem
{
public int Order { get; set; }
public string Description { get; set; }
}
}
Explanation:
The ObservableCollection raises the PropertyChanged event as you expect, but you cannot sort it.
So, you need the CollectionView to sort it and bind the sorted collection to you ListView/ListBox.
As you can see, adding an element after the DataContext initialization affects the UI sorting the last added item ("Zero") correctly.
You need to use ObservableCollection because this raises a collection changed event which your wpf ListView will pick up on.
How about doing
Public ObservableCollection<object> MyList
{
get
{
return new ObservableCollection<object>(MySortedList);
}
}
and then whenever you change your sorted list raise a property changed event for MyList.
This obviously depends how you would like to sort your list as it might be possible to sort the ObservableCollection your question needs more info.

Categories