Binding Collection in wpf comboBox with mvvm (C#) - c#

I am trying to using mvvm pattern with wpf to create an interface for a project previously did in win form.
In this project i have an object that contains some List<> that i have to show in real time on my interface with a combobox, the problem is that combobox don't change his values. I'm using the dll of mvvm fundation for implement NotifyPropertyChanged. I think to make some mistake but i don'y know where is it.
I've tried to do a simple code with only one list in viewmodel and without a model but the result doesn't change.
<Window x:Class="ProvaComboBox.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:ProvaComboBox"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<Grid>
<Grid.DataContext>
<local:ViewModel />
</Grid.DataContext>
<Button Content="Generate" Command="{Binding Generate}"/>
<Button Content="Clear" Command="{Binding Clear}"/>
<ComboBox ItemsSource="{Binding Path=Word, Mode=OneWay}" />
</Grid>
</Window>
//view Model
class ViewModel:ObservableObject
{
private List<string> _word;
public List<string> Word
{
get { return _word; }
}
public ViewModel()
{
_word = new List<string>();
}
public ICommand Generate
{ get { return new RelayCommand(GenerateExecute); } }
void GenerateExecute()
{
_prova.Add("pippo");
_prova.Add("pluto");
RaisePropertyChanged("Word");
}
public ICommand Clear
{ get { return new RelayCommand(ClearExecute); } }
void ClearExecute()
{
_prova.Clear();
RaisePropertyChanged("Word");
}
}
//View:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
}
I think that the problem it's RaisePropertyChanged, but it work correctly with normal variables.
I've tryed also using ObservableCollection and it work, but i can't use it with real project.
(p.s. Its my first question in stack overflow, sorry if i did some mistake!)

use ObservableCollection like that
public ObservableCollection<string> Word
{
get => _word;
set
{
_word= value;
RaisePropertyChanged("Word");
}
}
and change the binding mode in your combobox xaml code from OneWay to TwoWay or just remove it to be something like
<ComboBox ItemsSource="{Binding Path=Word}" />

Related

How to set datacontext for the Datagrid

I 'm learning wpf through mvvm. I have a ViewModel which is binded to the view through the window datacontext
Window.Xaml:
xmlns:Converter="clr-namespace:UploadFileToDB.ViewModel"
<Window.DataContext>
<Converter:ViewModel/>
</Window.DataContext>
The application works fine when all the methods are present inside the ViewModel Class.
Now I have a datagrid in the view, for populating the datagrid I have created a seperate class called populatedatagrid under the ViewModel namespace.
pseudocode which is not working :
namespace UploadFileToDB.ViewModel
{
public class PopulateDatagrid
{
public ObservableCollection<datagridmodel> modelclasswithcombobox
{
get;
set;
}
DataSet ds;
public void comboboxdata()
{ //This code populate the observable collection}
}
}
public class ViewModel : INotifyPropertyChanged
{
PopulateDatagrid populatedatagridwithobservablecollection = new PopulateDatagrid();
public ViewModel()
{
populatedatagridwithobservablecollection.combobxdata();
//Calling the above class method here to populate data. But this throws the error that BindingExpression path error that ModelClassWithcombobox property not found on object ViewModel
}
}
}
The above code is not working correctly due to the binding expression error.
pseudocode which is working :
namespace UploadFileToDB.ViewModel
{
public class ViewModel : INotifyPropertyChanged
{
public ObservableCollection<datagridmodel> modelclasswithcombobox
{
get;
set;
}
DataSet ds;
public void comboboxdata()
{ //This code populate the observable collection}
}
public ViewModel()
{
comboboxdata();
}
}
}
The above code works perfectly without any issue. On my understanding since I have mapped only ViewModel class on datacontext. The xaml can't able to find the observable collection which is present in the other class.
Can anyone help me on how to acheive segreagration of class and provide solution to above scenario
Edit : As requested window.xaml
<Window
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d" x:Class="UploadFileToDB.Window1"
xmlns:vm ="clr-namespace:UploadFileToDB.ViewModel"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:Converter="clr-namespace:UploadFileToDB.ViewModel"
Title="Window1" Height="400" Width="800">
<Window.DataContext>
<Converter:ViewModel/>
</Window.DataContext>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="1500*"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="41*"/>
</Grid.RowDefinitions>
<Tabcontrol>
<TabItem Header="Re Assign">
<StackPanel>
<DataGrid Height="150" x:Name="datagrid1" IsReadOnly="True" AutoGenerateColumns="True" CanUserAddRows="False" Width="507" Margin="150,-35,20,79" HorizontalAlignment="Left">
<DataGrid.Style>
<Style TargetType="{x:Type DataGrid}">
<Setter Property="ItemsSource" Value="{Binding modelclasswithcombobox}"/>
</Style>
</DataGrid.Style>
</DataGrid>
</StackPanel>
</TabItem>
</TabControl>
</Grid>
</Window>
You can only bind to the properties, not to the fields. So for it works you should use something like this:
public class ViewModel : INotifyPropertyChanged
{
public PopulateDatagrid Populatedatagridwithobservablecollection
{
get
{
return _populatedatagridwithobservablecollection;
}
set
{
if (value != _populatedatagridwithobservablecollection)
{
_populatedatagridwithobservablecollection = value;
NotifyPropertyChanged(nameof(Populatedatagridwithobservablecollection));
}
}
}
private PopulateDatagrid _populatedatagridwithobservablecollection = new PopulateDatagrid();;
}

WPF ComboBox does not display content

I have a simple combo box on my xaml file:
<ComboBox Name="environmentComboBox" Grid.Column="1" Grid.Row="0" Margin="2"
SelectionChanged="environmentComboBox_SelectionChanged"
ItemsSource="{Binding Path=Test}"/>
Here is the code for its content:
private List<string> test = new List<string>(){"1", "2"};
public List<string> Test
{
get
{
return test;
}
set
{
test = value;
}
}
I tried to debug the application, the ComboBox does not show anything.
But when I checked if Test has content, it shows the two strings.
Have to set the views DataContext to the Model/Window containing the List<T>?
If not you need to tell the View what DataContext to use, below is a quick example of a WPF window, and setting the xamls DataContext to the code behind of the View.
Also its recommended to use ObservableCollection<T> when binding collections as adding and removing items will update the ComboBox automatically
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
DataContext = this; // set datacontext
}
private ObservableCollection<string> test = new ObservableCollection<string>() { "1", "2" };
public ObservableCollection<string> Test
{
get { return test; }
set { test = value; }
}
}
<Window x:Class="WpfApplication1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">
<StackPanel>
<ComboBox ItemsSource="{Binding Path=Test}"/>
</StackPanel>
</Window>

How will i show the first item of an observablecollection in a Listbox?

i have this declaration:
public ObservableCollection<SomeType> Collection { get; set; }
i tried something, like:
myListBox.ItemsSource = Collection[0];
to show the first item of Collection in the Listbox control, but it gives error.
how will i do this? what change should i make on the right side?
You need to bind the ItemSource to your collection, and then set the selected index to the one you want (0 in your case)
Here's the smallest example. XAML:
<Window x:Class="WPFSOTETS.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525"
DataContext="{Binding RelativeSource={RelativeSource Self}}">
<Grid>
<ComboBox ItemsSource="{Binding Collection}" SelectedIndex="2"></ComboBox>
</Grid>
</Window>
Code behind:
using System.Collections.ObjectModel;
using System.Windows;
namespace WPFSOTETS
{
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
public ObservableCollection<string> Collection
{
get
{
return new ObservableCollection<string> {"one","two","three"};
}
}
}
}
I set the index to 2, just for fun, but you can play with it.
As of comment, if you want to set this in the codebehind, you'll have to name your control, and then you can use it from your codebehind and do your binding and setting there. You can have a look at this answer for example.

A Simple Wpf MVVM Binding Issue

I am trying my hands on WPF MVVM. I have written following code in XAML
<UserControl x:Class="Accounting.Menu"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:Accounting"
mc:Ignorable="d"
d:DesignHeight="105" d:DesignWidth="300">
<UserControl.DataContext>
<local:MenuViewModel/>
</UserControl.DataContext>
<StackPanel>
<StackPanel>
<TextBlock Text="{Binding Path=MenuHeader}"/>
</StackPanel>
<ListBox ItemsSource="{Binding Path=MenuItems}" Height="70"/>
</StackPanel>
</UserControl>
I have got a MenuViewModel with properties MenuHeader and MenuItems. I get values in both the properties during runtime. Former is bound to text of TextBlock and latter to ItemSource of ListBox. But when I run the solution, TextBlock and ListBox are empty.
Edit: Code of ViewModel
public class MenuViewModel: ViewModelBase
{
AccountingDataClassesDataContext db;
private string _menuType;
public string MenuHeader { get; set; }
public ObservableCollection<string> MenuItems { get; set; }
public MenuViewModel()
{
}
public MenuViewModel(string menuType)
{
this._menuType = menuType;
db = new AccountingDataClassesDataContext();
if (menuType == "Vouchers")
{
var items = db.Vouchers.OrderBy(t => t.VoucherName).Select(v => v.VoucherName).ToList<string>();
if (items.Any())
{
MenuItems = new ObservableCollection<string>(items);
MenuHeader = "Vouchers";
}
}
else
{
System.Windows.MessageBox.Show("Menu not found");
}
}
}
Thanks in advance.
You are creating your ViewModel in the XAML using your ViewModel's default contructor which does nothing. All your population code is in the non-default contructor which is never called.
The more usual way is to create the ViewModel in code, and inject it into the view either explicitly using View.DataContext = ViewModel, or impllcitly using a DataTemplate.
I think you have to trigger the OnPropertyChanged event. I am not sure if you are using a MVVM library (since you inherit from ViewModelBase you might be using MVVM Light for example), there they wrap the OnPropertyChanged in the RaisePropertyChanged event handler.
Triggering the event will inform WPF to update the UI.
string m_MenuHeader;
public string MenuHeader
{
get
{
return m_MenuHeader;
}
set
{
m_MenuHeader=value; OnPropertyChanged("MenuHeader");
}
}

How to bind a Grid in WPF when using MVC?

I know a way to do MVC binding of one string to one TextBox. That's how it can be done:
C#:
namespace WpfApplication4
{
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
DataContext = Model;
}
public ModelClass Model = new ModelClass();
private void button1_Click(object sender, RoutedEventArgs e)
{
Model.Output += "Setting New Output! ";
}
public class ModelClass : INotifyPropertyChanged
{
string _output;
public event PropertyChangedEventHandler PropertyChanged =
delegate { };
public string Output
{
get { return _output; }
set { _output = value;
PropertyChanged(this,
new PropertyChangedEventArgs("Output"));
}
}
}
}
}
XAML:
<Window x:Class="WpfApplication4.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">
<Grid>
<Button Content="Button" VerticalAlignment="Top"
Name="button1" Click="button1_Click" />
<TextBox VerticalAlignment="Bottom"
Name="textBox1" Text="{Binding Path=Output}" />
</Grid>
</Window>
But I can't find a way to bind a two-dimensional array (or List) to a Grid or DataGrid. Can you help me with it? I couldn't find a working example on SO.
Consider using a DataGrid to display your two-dimensional array, assuming you can store your data as a List<ColumnData> where ColumnData is a class with one property per table column.
The WPF SDK contains a DataGrid, and there are several data grids from vendors available that have additional features.
if you wanna bind data to a datagrid you should be read something about the following.
ICollectionView, BindingListCollectionView
if you have somekind of collection you simply set the itemssource.
<DataGrid ItemsSource="{Binding Path=MyCollection, Mode=OneWay}" />
Collection types are mostly ObservableCollection or DataSet/DataTable. if your collection supports editing and so on, you can do it with the datagrid.

Categories