How to populate a combobox with access using MVVM - c#

I'm new to C# and I'm trying to create a code using MVVM pattern, but I don't know how to populate a combobox using that pattern. Please Give me help to create the ViewModel and the binding to the xaml.
Code Model:
public int Cd_Raca
{
get;
set
{
if(Cd_Raca != value)
{
Cd_Raca = value;
RaisePropertyChanged("Cd_Raca");
}
}
}
public string Nm_Raca
{
get;
set
{
if(Nm_Raca != value)
{
Nm_Raca = value;
RaisePropertyChanged("Nm_Raca");
}
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void RaisePropertyChanged(string property)
{
if(PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(property));
}
}
}
Xaml:
<ComboBox x:Name="dsCmbRaca" HorizontalAlignment="Left" Margin="438,4,0,0"
VerticalAlignment="Top" Width="94" Height="19"/>

Use the ItemsSource Property and set it to an enumeration of objects. With DisplayMemberPath you can set it to a property of a single object of your list if the list is not just a list of strings.
I.e. in my sample the object of the list has a Description property for display and a Value property for the selected value.
All bindings in the sample need to be a property in your ViewModel (=DataContext).
<ComboBox DisplayMemberPath="Description" HorizontalAlignment="Left"
VerticalAlignment="Top" Width="120"
ItemsSource="{Binding myList}"
SelectedValue="{Binding mySelectedValue}" SelectedValuePath="Value" />
Edit:
The List property could look like this:
public IList<MyObject> myList { get { return new List<MyObject>();} }
The Object could look like this for example:
public class MyObject
{
public string Description { get; }
public enum Value { get;}
}
The Object is optional. You could just pass a list of strings.
Disclaimer: I hacked this in notepad. I hope it compiles.
UPDATE
Looking at your code at least from what you post your properties are not implemented correctly. You need a backing field if you code it like you have:
private int _cd_Raca;
private string _nm_Raca;
public int Cd_Raca
{
get{ return _cd_Raca;}
set
{
if(_cd_Raca != value)
{
_cd_Raca = value;
RaisePropertyChanged("Cd_Raca");
}
}
}
public string Nm_Raca
{
get{return _nm_Raca;}
set
{
if(_nm_Raca != value)
{
_nm_Raca = value;
RaisePropertyChanged("Nm_Raca");
}
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void RaisePropertyChanged(string property)
{
if(PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(property));
}
}
Reading your comment to my first answer seems you might have a specific use case. So if this update does not help maybe you can add some more information to your question.

Related

C# How to edit cell value in gridview?

I have a gridview shown as below in XAML
<ListView x:Name="listTasks">
<ListView.View>
<GridView x:Name="gridTasks">
<GridViewColumn Header="ID" HeaderStringFormat="Lowercase" Width ="26" DisplayMemberBinding="{Binding id}"/>
<GridViewColumn Header="Something" Width="113" DisplayMemberBinding="{Binding something}"/>
<GridViewColumn Header="State" Width="179" DisplayMemberBinding="{Binding currentState}"/>
</GridView>
</ListView.View>
</ListView>
and i have a button which adds to this gridview using the below
m.myList.Add(new mylistview.myitems
{
id = m.id,
something= m.something,
currentState = m.currentState,
});
This button works perfectly by adding the row into the gridview. However I would like to modify theCurrentState using a method that is running. How would I locate for example, ID = "8" and then modify theCurrentState for that row?
UPDATED CODE SHOWN
I've now replaced my list<Task> with ObservableCollection and managed to get it to add to my listview when I click onto my button. However, I am struggling to implement the iNotifyPropertyChanged into my code and getting it to work correctly... Below is my listview class
public class mylistview : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged([System.Runtime.CompilerServices.CallerMemberName] string propertyName = "")
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
private string _currentState;
public string currentState
{
get { return _currentState; }
set
{
_currentState = value;
OnPropertyChanged();
}
}
public ObservableCollection<myitems> _myList = new ObservableCollection<myitems>();
public ObservableCollection<myitems> myList
{
get { return _myList; }
}
private static int _id = 0;
public class myitems
{
public int id { get; set; }
public string something{ get; set; }
public string currentState { get; set; }
}
public int id
{
get { return _id; }
set { _id = value; }
}
}
So I see you're using data bindings already, that's good. But your question makes me think you haven't quite grasped everything it can do for you yet.
My recommendation would be to forget about adding items directly to listOfTasks.Items. Instead you should make an ObservableCollection to hold that list and bind the listOfTasks to it. Like so:
ObservableCollection tasks = new ObservableCollection<mylistview.myitems>();
ListOfTasks.ItemsSource = tasks;
With that binding in place you should be able to simply add new items to the tasks list when they click your button:
tasks.Add(new mylistview.myitems
{
id = theId,
something= something,
currentState = theCurrentState,
});
and it should automatically update the GUI.
The last step is to make sure that the class mylistview.myitems implements INotifyPropertyChanged. This is easier than it sounds; you just need to have it trigger an event any time the property is set. Something like so:
public class exampleProperties: INotifyPropertyChanged
{
//this is the event you have to emit
public event PropertyChangedEventHandler PropertyChanged;
//This is a convenience function to trigger the event.
//The CallerMemberName part will automatically figure out
//the name of the property you called from if propertyName == ""
protected void OnPropertyChanged([System.Runtime.CompilerServices.CallerMemberName] string propertyName = "")
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName);
}
}
//Any time this property is set it will trigger the event
private string _currentState = "";
public string currentState
{
get { return _currentState; }
set
{
if (_currentState != value)
{
_currentState = value;
OnPropertyChanged();
}
}
}
}
Now that the gridview is bound to an ObservableCollection and the items held in that collection can notify interested GUI controls that their properties have changed, you should simply be able to update the GUI simply by changing the appropriate item in the collection.
And here's an example of a form that uses the whole technique: https://msdn.microsoft.com/en-us/library/system.componentmodel.inotifypropertychanged(v=vs.110).asp
edit
I forgot that you specifically need to bind to the ItemSource property of the ListView. The way I have done it in the past is to set ItemsSource={binding} in the ListView's xaml and then assign an ObservableCollection to ListView.DataContext. However I have found an easier way and updated the original post with it. Here's a reference: http://www.wpf-tutorial.com/listview-control/listview-with-gridview/
Edit 2
Aha, you're adding the iPropertyChangedNotify to the wrong thing. It goes on the myitems class like so:
public class myitems : iNotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged([System.Runtime.CompilerServices.CallerMemberName] string propertyName = "")
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
private int _id;
public int id
{
get { return _id; }
set
{
_id = value;
OnPropertyChanged();
}
}
public string something{ get; set; }
public string currentState { get; set; }
}
I leave updating the current state and something properties as an excersize. They also need to trigger the OnPropertyChanged event when their value is set.
Maybe with
listOfTasks.Items.Cast<ListViewItem>().First(item => item.ID == "8").theCurrentState = newState;
//I'm not sure about the Cast stuff, because I don't know what types the ListView uses for its items
Of course you could iterate through the items with a loop and check manually for the ID as well.

UWP MVVM Data Binding for dummies (textbox.text from String)

Well, having a go at MVVM with UWP template 10. I have read many pages, and although everyone tries to say its really easy, I still can't make it work.
To put it into context, OCR is being run on an image, and I would like the text to be displayed in textbox automatically.
Here is my Model:
public class TextProcessing
{
private string _ocrText;
public string OcrText
{
get { return _ocrText; }
set
{
_ocrText = value;
}
}
}
Here is my ViewModel:
public class ScanPageViewModel : ViewModelBase, INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private TextProcessing _ocrTextVM;
public ScanPageViewModel()
{
_ocrTextVM = new TextProcessing();
}
public TextProcessing OcrTextVM
{
get { return _ocrTextVM; }
set {
_ocrTextVM = value;
this.OnPropertyChanged("OcrTextVM");
}
}
public void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
Here is my View:
<TextBox x:Name="rtbOcr"
Text="{Binding OcrTextVM.OcrText, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
Firstly, that is not working. Could someone try to show where I am going wrong?
Then, the data is coming from a Services file, how would the Services update the value? What would be the correct code?
Thanks in advance.
Following code is cite from code.msdn (How to achieve MVVM design patterns in UWP), it will be helpful for you:
Check you code step by step.
1.ViewModel implemented interface INotifyPropertyChanged,and in property set method invoked PropertyChanged, like this:
public sealed class MainPageViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private string _productName;
public string ProductName
{
get { return _productName; }
set
{
_productName = value;
if (PropertyChanged != null)
{
PropertyChanged.Invoke(this, new PropertyChangedEventArgs(nameof(ProductName)));
}
}
}
}
2.Initialize you ViewMode in you page, and set DataContext as the ViewMode, like this:
public sealed partial class MainPage : Page
{
public MainPageViewModel ViewModel { get; set; } = new MainPageViewModel();
public MainPage()
{
...
this.DataContext = ViewModel;
}
}
3.In you xaml, binding data from viewMode, like this:
<TextBox Text="{Binding Path=ProductName,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}" Name="ProductNameTextBox" TextChanged="ProductNameTextBox_TextChanged" />
Your OnPropertyChanged call on OcrTextVM isn't actually called in your case, since you set the value in the constructor to its backing field and bypass the property.
If you set the value via the property, it should work:
public ScanPageViewModel()
{
OcrTextVM = new TextProcessing();
}
Of course your view needs to know that ScanPageViewModel is its DataContext. Easiest way to do it is in the constructor of the code-behind of your view:
public OcrView()
{
DataContext = new ScanPageViewModel();
InitializeComponent();
}
Assuming your OCR service is returning a new TextProcessing object on usage, setting the property of OcrTextVM should suffice:
public class ScanPageViewModel : ViewModelBase, INotifyPropertyChanged
{
//...
private void GetOcrFromService()
{
//...
TextProcessing value = OcrService.Get();
OcrTextVM = value;
}
}
On a note, the OcrTextVM name doesn't really reflect what the property is doing, since it doesn't look like it's a viewmodel. Consider renaming it.
Actually, it is very easy once I manage to understand. Here is the code needed to update a TextBox.Text
In the Models:
public class DisplayText : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private string _text;
public string Text
{
get { return _text; }
set
{
_text = value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(Text)));
}
}
}
In the XAML file:
<TextBox Text="{Binding Helper.Text, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" ... />
In the ViewModels:
private DisplayText _helper = new DisplayText();
public DisplayText Helper
{
get { return _helper; }
set
{
_helper = value;
}
}
Then any mod from the ViewModels:
Helper.Text = "Whatever text, or method returning a string";

INotifyPropertyChanged is not working MVVM

I have an Observable collection, but after updating the collecion, my Listview is not updating even after raising Property Changed event see below code:-
Look below XAML:-
<ListView Grid.Row="1" Grid.Column="0" Name="lvGroups" Margin="0,34,0,0"
Grid.RowSpan="2" ItemsSource="{Binding Path=*VideoGroupList*,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}">
<GridView>
<GridViewColumn Width="150" DisplayMemberBinding="{Binding *Name*}" />
</GridView>
Look below Class
public class VideoGroupViewModel : ObservableEnitiy
{
public ObservableCollection<Group> VideoGroupList { get; set; }
}
public abstract class ObservableEnitiy : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string propertyName)
{
this.VerifyPropertyName(propertyName);
if (this.PropertyChanged != null)
{
var e = new PropertyChangedEventArgs(propertyName);
this.PropertyChanged(this, e);
}
}
}
[Serializable]
public class Group : PropertyChangedNotification
{
public int ID { get; set; }
[Required(ErrorMessage = "Group name is required.")]
public string *Name*
{
get { return GetValue(() => Name); ; }
set
{
SetValue(() => Name, value);
}
}
[XmlIgnore]
public bool IsSelected { get; set; }
}
protected T GetValue<T>(Expression<Func<T>> propertySelector)
{
string propertyName = GetPropertyName(propertySelector);
return GetValue<T>(propertyName);
}
I am calling this way
VideoGroupList = new ObservableCollection<Group>(videoGroupManager.GetVideoGroups());
OnPropertyChanged("VideoGroupList");
Add the following using statements:
using System.Collections.Generic;
using System.Linq;
Change your VideoGroupList line to:
VideoGroupList.Clear();
videoGroupManager.GetViewGroups().ToList().ForEach(x => VideoGroupList.Add(x));
This better utilises the change notification built into the ObservableCollection
According to the MSDN Documentation, CollectionChanged will be raised on adding, removing and changing an element in an ObservableCollection. I found this not to be the case however.
ObservableCollection.CollectionChanged will only be thrown on add/remove.
I use this FullyObservableCollection here instead, with the modification to have T be my ViewModelBase class instead of INotifyPropertyChanged directly.
When I declare my Property then, I use a snippet with the following output:
private FullyObservableCollection<Group> _videoGroupList;
public FullyObservableCollection<Group> VideoGroupList
{
get { return _videoGroupList; }
set
{
if(_videoGroupList == value) return;
var nil = _videoGroupList == null;
_videoGroupList = value;
// If null before and not after (declaring it after being null),
// call OnPropertyChanged on Change of Collection.
if(nil && _videoGroupList != null)
_videoGroupList.CollectionChanged += (s,e)
=> OnPropertyChanged(nameof(VideoGroupList));
OnPropertyChanged();
}
}
This way you will always have INotifyPropertyChanged Notifications on Add, Remove, Replace and Change action on the collections and every single one of its members. It will save you a lot of hassle since you do not need to reinstanciate the whole collection if one of hundreds of thousands properties in it changes.
Remember to initialize it in your constructor though. :)

How to bind ComboBox properly in WPF?

I'm new to WPF and MVVM and I'm developing a test WPF application following the MVVM design pattern. My database has 2 entities, Cards and Departments. Any card can have only 1 department, so it's a one-to-many relationship.
I've created the following ViewModel in order to bind to the view:
public class CardViewModel : INotifyPropertyChanged
{
public CardViewModel(Card card)
{
this.Card = card;
SqlConnectionStringBuilder builder = new SqlConnectionStringBuilder();
builder.DataSource = ".\\SQLExpress";
builder.InitialCatalog = "TESTDB";
builder.IntegratedSecurity = true;
SybaseDatabaseContext myDB = new SybaseDatabaseContext(builder.ConnectionString);
var query = from d in myDB.Departments
select d;
this.Departments = new ObservableCollection<Department>(query);
}
private Card _Card;
private ObservableCollection<Department> _Departments;
public Card Card
{
get { return _Card; }
set
{
if (value != this._Card)
{
this._Card = value;
SendPropertyChanged("Card");
}
}
}
public ObservableCollection<Department> Departments
{
get { return _Departments; }
set
{
this._Departments = value;
SendPropertyChanged("Departments");
}
}
#region INPC
// Logic for INotify interfaces that nootify WPF when change happens
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void SendPropertyChanged(String propertyName)
{
if ((this.PropertyChanged != null))
{
this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
#endregion
}
The CardForms' datacontext is currently being set to an instance of the CardViewModel in the code where the CardForm is being instantiated, but I'm going to create a IoC container or dependency injections down the line.
Everything binds correctly except for the ComboBox that should contain all departments and that has the current department in the Card instance selected (card.Department). Here's the XAML for the ComboBox:
<ComboBox Height="23" HorizontalAlignment="Left" Margin="350,64,0,0"
Name="comboBoxDepartment" VerticalAlignment="Top" Width="120"
IsSynchronizedWithCurrentItem="True"
ItemsSource="{Binding Path=Departments}"
DisplayMemberPath="DepartmentName"
SelectedItem="{Binding Path=Card.Department, Mode=TwoWay}" />
The departments are displayed in the combobox, but the current department of the card isn't and if I try to change it I get and error saying "Cannot add an entity with a key that is already in use".
So, my question is, how do I bind this combobox correctly to my ViewModel?
P.S. I know populating the ObservableCollection<Department> in the ViewModel is probably not the right way to do it, but I could not think of a better way at the time. If you have any suggestions for this also, please let me know.
Additionally, this is the Card model:
[Table(Name = "Card")]
public class Card : INotifyPropertyChanged, INotifyPropertyChanging
{
private string _CardID;
private string _Holder;
private Int16? _DepartmentNo;
[Column(UpdateCheck = UpdateCheck.WhenChanged)]
public string CardID
{
get
{
return this._CardID;
}
set
{
if (value != this._CardID)
{
SendPropertyChanging();
this._CardID = value;
SendPropertyChanged("CardID");
}
}
}
[Column(UpdateCheck = UpdateCheck.WhenChanged)]
public string Holder
{
get
{
return this._Holder;
}
set
{
if (value != this._Holder)
{
SendPropertyChanging();
this._Holder = value;
SendPropertyChanged("Holder");
}
}
}
[Column(CanBeNull = true, UpdateCheck = UpdateCheck.WhenChanged)]
public Int16? DepartmentNo
{
get
{
return this._DepartmentNo;
}
set
{
if (value != this._DepartmentNo)
{
SendPropertyChanging();
this._DepartmentNo = value;
SendPropertyChanged("DepartmentNo");
}
}
}
private EntityRef<Department> department;
[Association(Storage = "department", ThisKey = "DepartmentNo", OtherKey = "DepartmentNo", IsForeignKey = true)]
public Department Department
{
get
{
return this.department.Entity;
}
set
{
Department previousValue = this.department.Entity;
if (((previousValue != value)
|| (this.department.HasLoadedOrAssignedValue == false)))
{
this.SendPropertyChanging();
if ((previousValue != null))
{
this.department.Entity = null;
previousValue.Cards.Remove(this);
}
this.department.Entity = value;
if ((value != null))
{
value.Cards.Add(this);
this._DepartmentNo = value.DepartmentNo;
}
else
{
this._DepartmentNo = default(Nullable<short>);
}
this.SendPropertyChanged("Department");
}
}
}
I edited the constructor in the CardViewModel to take the DataContext as a parameter and that did it. This is the new CardViewModel constructor:
public CardViewModel(Card card, SybaseDatabaseContext myDB)
{
this.Card = card;
var query = from d in myDB.Departments
select d;
this.Departments = new ObservableCollection<Department>(query);
}
Had to do a bit of research on this myself. Thought I would contribute with a self answered question, but found this open current question...
The ComboBox is designed to be a kind of textbox that restricts it's possible values to the contents of a given list. The list is provided by the ItemsSource attribute. The current value of the ComboBox is the SelectedValue property. Typically these attributes are bound to relevant properties of a corresponding ViewModel.
The following example shows wired ComboBox together with a TextBox control used to redundantly view the current value of the ComboBox by sharing a view model property. (It is interesting to note that when TextBox changes the shared property to a value outside the scope of the ComboBox's list of values, the ComboBox displays nothing.)
Note: the following WPF/C# example does does use code-behind and so presents the ViewModel as merely the datacontext of the view and not a partial class of it, a current implementation constraint when using WPF with F#.
WPF XAML
<Window
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:m="clr-namespace:WpfApplication1"
Title="MainWindow" Height="350" Width="525">
<Window.DataContext>
<m:MainWindowVM />
</Window.DataContext>
<StackPanel>
<TextBox Text="{Binding SelectedString}" />
<ComboBox ItemsSource="{Binding MyList}" SelectedValue="{Binding SelectedString}" />
</StackPanel>
</Window>
C# ViewModel
using System.Collections.Generic;
using System.ComponentModel;
namespace WpfApplication1
{
public class MainWindowVM : INotifyPropertyChanged
{
string selectedString;
void NotifyPropertyChanged(string propertyName)
{
if (PropertyChanged == null) return;
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
public string SelectedString
{
get { return selectedString; }
set
{
selectedString = value;
NotifyPropertyChanged("SelectedString");
}
}
public List<string> MyList
{
get { return new List<string> { "The", "Quick", "Brown", "Fox" }; }
}
public event PropertyChangedEventHandler PropertyChanged;
}
}
By default, ToString() is used to interpret the objects in the list. However, ComboBox offers DisplayMemberPath and SelectedValuePath attributes for specifying paths to specific object properties for corresponding displayed and stored values. These paths are relative to the list object element so a path of "Name" refers to Name on a list object item.
The "Remarks" section of this MSDN link explains the interpretations of the IsEditable and IsReadOnly ComboBox properties.

INotifyPropertyChanged.PropertyChanged fired, but UI not updated after form loads

Below is an example of my model , ViewModel and xaml binding. The viewmodel implements INotifyPropertChanged. The problem i'm having is...when the wpf form first loads i set ActiveStock and i see both setter and getter being called and the ui is updated to reflect the data correctly.
However, when i later set StockViewModel.ActiveStock, FirePropertyChanged is invoked but i don't see the getter being called, and consequently the UI does not update to reflect the new data. Any ideas what might be happening here?
The second question i have is whether i also need to raise PropertyChanged for the child properties (PriceData and CompanyData) of my model when ViewModel.ActiveStock is changed?
public class Stock
{
public string Ticker { get; set; }
public StockData PriceData { get; set; }
public StockData CompanyData { get; set; }
}
public class StockData
{
...
}
public class StockViewModel:INotifyPropertyChanged
{
private Stock _activeStock;
public Stock ActiveStock
{
get{ return _activeStock;}
set{ _activeStock = value; FirePropertyChanged("ActiveStock");}
}
...
}
XAML:
<UserControl Template="{StaticResource StockTemplate}" DataContext="{Binding ActiveStock}" Tag="{Binding PriceData}" />
<UserControl Template="{StaticResource StockTemplate}" DataContext="{Binding ActiveStock}" Tag="{Binding CompanyData}" />
Edit:
if i remove the DataContext binding for the UserControl and instead set the DataContext for these two controls in code behind when ActiveStock changes, it works fine. why???
The getter is not being called because as far as I can see nothing is "getting" the value, The only properties used are PriceData and CompanyData and these don't use INotifyPropertyChanged
You will have to implement INotifyPropertyChanged on your Stock class for the UI to reflect the changes.
public class Stock : INotifyPropertyChanged
{
private string _ticker;
private StockData _priceData;
private StockData _companyData;
public string Ticker
{
get { return _ticker; }
set { _ticker = value; NotifyPropertyChanged("Ticker"); }
}
public StockData PriceData
{
get { return _priceData; }
set { _priceData = value; NotifyPropertyChanged("PriceData"); }
}
public StockData CompanyData
{
get { return _companyData; }
set { _companyData = value; NotifyPropertyChanged("CompanyData"); }
}
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged(string property)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(property));
}
}
}
You might want to try to specify the mode property on your datacontext bindings.
DataContext="{Binding ActiveStock, Mode=OneWay}"
I'm not sure that OneTime is the default binding for DataContext, but it would explain so if the above helps.
The second question has been answered by sa_ddam213.
HTH

Categories

Resources