I'm using a two column (ID,NAME) DataGrid and want update the row with new values.
I'm not sure how I can use the binding part in my C# code.
<DataGrid Name="dataGridUser" ItemsSource="{Binding}" VerticalAlignment="Top" Width="auto" Grid.RowSpan="2"/>
How can I update the datagrid with net values like :
ID , Name
123, Peter
345, Simon
....
So to give you an Example, first create a Model
public class User
{
public int ID { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
}
then in your Code-Behind File create an ObservableCollection of that Model
private ObservableCollection<User> _myUsers;
public ObservableCollection<User> MyUsers
{
get
{
if (_myUsers == null)
{
_myUsers = new ObservableCollection<User>();
}
return _myUsers;
}
}
and now you can bind your DataGrid to this Property
<DataGrid Grid.Row="1" Name="dataGridUser" ItemsSource="{Binding MyUsers}" AutoGenerateColumns="True"/>
and donĀ“t forget to set the DataContext
<Window x:Class="WpfApplication1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
DataContext="{Binding RelativeSource={RelativeSource Self}}"></Window>
if you add a new User to the ObservableCollection MyUsers it will immediately be displayed in your DataGrid, but if you change the FirstName of a existing User it will not display the changes. To do this you must implement INotityPropertyChanged in your Model.
But if you plan to develop a more complex Application I would recommend to take a look at the MVVM-Pattern.
Personally I like the MVVM Light Toolkit this Video should give you a good Idea what MVVM is all about.
Related
Sorry if the wording of my question is not great or if this has been answered somewhere, I've searched, but I don't really know how well to explain what I am trying to do.
Here's a simple testbed I've partially set up to help explain:
MainWindow.xaml:
<Window x:Class="wpfExample.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:wpfExample"
mc:Ignorable="d"
Title="MainWindow" Height="350" Width="525">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<ListView Grid.Column="0" ItemsSource="{Binding People}" DisplayMemberPath="Name"/>
<ListBox Grid.Column="1" ItemsSource="{Binding Interests}" Margin="0,4,4,4">
<ListBox.ItemTemplate>
<DataTemplate>
<CheckBox Content="{Binding Name}"/>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Grid>
MainWindow.xaml.cs:
using System;
using System.Collections.ObjectModel;
using System.Windows;
namespace wpfExample
{
public class Person
{
public string Name { get; set; }
public ObservableCollection<Guid> Interests { get; set; }
}
public class Interest
{
public Guid Id { get; set; }
public string Name { get; set; }
}
public class Data
{
public ObservableCollection<Person> People { get; set; }
public ObservableCollection<Interest> Interests { get; set; }
}
public partial class MainWindow : Window
{
public MainWindow()
{
DataContext = new Data
{
People = new ObservableCollection<Person>
{
new Person {Name="Fred", Interests=new ObservableCollection<Guid>() },
new Person {Name="Jane", Interests=new ObservableCollection<Guid>() },
new Person {Name="Zach", Interests=new ObservableCollection<Guid>() }
},
Interests = new ObservableCollection<Interest>
{
new Interest {Name="Gardening", Id=Guid.NewGuid() },
new Interest {Name="Writing", Id=Guid.NewGuid() },
new Interest {Name="Avoiding Tax", Id=Guid.NewGuid() }
}
};
InitializeComponent();
}
}
}
So I have a DataContext that contains two lists. One contains Interests, which have a name and an ID. The other contains People which have a name and a list of IDs of interests.
When a Person is selected in the UI, I want to be able to add and remove IDs of interests to their respective list, hence the ListView in column 1 is bound to the list of Interests, but how do I correctly bind up the IsChecked property of the checkboxes in the list?
In my full project, I've been able to successfully read properties of the selected Person's interest list by using a MultiBinding for IsChecked with a MultiValueConverter to pass both the Id of the Interest and the List of Interests of the Person through together (since you can't use binding with the parameter for a 'normal' value converter). I feel that this solution is a little bit of an abuse of the converter, but I'm happy to stick with it if necessary.
How do I implement a system that will allow me to add and remove Interest Guids to a Person's list of interests when the checkbox is toggled? Is there a cleaner way of doing this? I don't want to change the model if it can be avoided.
I wouldn't say your MultiConverter solution is an abuse at all; you are doing exactly what converters should (take a set of data and converting to the target type, then going back).
That being said; converters are a bit of a mess due to their generality (use of object), and that goes double for Multi Converters, so if you want a different solution I would recommend creating a view model wrapper for Interest; say Selectable<T>:
public class Selectable<T>
{
public T Data {get; set;}
public bool Selected {get; set;}
}
Then update your property
public ObservableCollection<Selectable<Interest>> Interests { get; set; }
And bind IsChecked directly to Selected
Then whenever the selected Person is changed you can update the Selected property appropriately; store the previous value to the other Person, etc. It somewhat depends on when the user action needs to be reflected in the model.
All that being said, both approaches will be perfectly valid, it just comes down to what you are most comfortable with.
I have a class defined like:
public class Agent
{
public int Id { get; set; }
public string Category { get; set; }
// rest removed for brevity
}
Then, in WPF, I get the data as List and pass it to DataContext as this:
List<Agent> agents; // this includes my data
this.DataContext = agents;
And in .xaml part I want to list the Category field of each object. I have something like this:
<ListBox
Name="agentCategoryListBox"
Grid.Row="2"
Grid.Column="1"
ItemSource="{Binding Path=Category"} />
But this doesn't seem to work correctly. Any ideas?
Let me help you to do this in the correct way as Alex suggested.
Create a list and populate it in ViewModel like this
ViewModel
public class MainWindowViewModel : INotifyPropertyChanged
{
public MainWindowViewModel()
{
agents = new ObservableCollection<Agent>();
LoadData();
}
private void LoadData()
{
agents.Add(new Agent { Id = 1, Category = "a" });
agents.Add(new Agent { Id = 2, Category = "b" });
agents.Add(new Agent { Id = 3, Category = "c" });
}
}
In XAML, Make your list and use data template like this:
<Window.Resources>
<DataTemplate x:Key="AItemTemplate">
<TextBlock Text="{Binding Category}"></TextBlock>
</DataTemplate>
</Window.Resources>
<ListBox ItemsSource="{Binding agents}"
ItemTemplate="{StaticResource AItemTemplate}"></ListBox>
That is it !!
Normally the DataContext would be a view model class that would contain the list of agents; then you can bind the ItemsSource to that list. Any of the many examples that deal with listbox will be pretty straight forward when it comes to that. Not really sure how the binding should look like if the list itself is the DataContext.
Then once the ItemsSource is set to a list of agents, if you want to show the Category in the list, the simpler way is to set DisplayMemberPath to "Category".
I suggest looking into MVVM and learning to apply it, it's an invaluable concept in my opinion.
You try to bind your listbox to a string property.
You can try this :
Give a name to your user control for exemle myUC
Add a property to your user control :
public List<Agent> AgentList { get; set; };
Fill your agentlist :
this.AgentList = //fill method
And bind your listbox like this :
<ListBox
Name="agentCategoryListBox"
Grid.Row="2"
Grid.Column="1"
ItemSource="{Binding Path=AgentList, ElementName=myUC"} />
may be this will give you an idea:
http://www.c-sharpcorner.com/forums/wpf-datacontext-binding-with-listbox
The fastest way to get what you want is :
<ListBox
Name="agentCategoryListBox"
Grid.Row="2"
DisplayMemberPath="Category"
Grid.Column="1"
ItemSource="{Binding Path=."} />
ItemsSource is binded directly to your Datacontext (which is your list) And then you tell to your ListBox to display the property Category.
But the proper way would have been :
1 - Create a DataContext
public class AgentsDC
{
public List<Agent> Agents { get; set; }
}
2 - Give this class as DataContext
this.DataContext = new AgentsDC();
3 - Bind all these things
<ListBox
Name="agentCategoryListBox"
Grid.Row="2"
DisplayMemberPath="Category"
Grid.Column="1"
ItemSource="{Binding Path=Agents"} />
I also would suggest you to use MVVM. But if you do not want to then try this.
XAML:
<ListBox Name="AgentCategoryListBox" ItemsSource="{Binding}">
<ListBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Category}" d:DataContext="{d:DesignData}" />
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
CS:
public MainWindow()
{
InitializeComponent();
List<Agent> agents = new List<Agent>
{
new Agent
{
Category = "Category"
}
};
DataContext = agents;
}
public class Agent
{
public string Category
{
get;
set;
}
}
I have a viewmodel setup as the following
public class cDriveListVM
{
public string Drive { get; set; }
public cDriveListVM(string name)
{
Drive = name;
}
}
I declare the observablecollection in the window and set its datacontext to this observable collection.
public ObservableCollection<cDriveListVM> DriveList { get; set; }
private void dl()
{
DriveList = new ObservableCollection<cDriveListVM>();
DriveList.Add(new cDriveListVM("drive 1"));
DriveList.Add(new cDriveListVM("drive 2"));
this.DataContext = DriveList;
}
Xml for combobox:
<ComboBox x:Name="Drive_ComboBox" ItemsSource="{Binding Path=Drive}" HorizontalAlignment="Center" IsReadOnly="True" Grid.Column="0" Grid.Row="0" Width="300" Margin="10" SelectionChanged="Drive_Changed" Height="22" VerticalAlignment="Top"/>
I am just learning how to use Viewmodel so I am unsure what I am doing wrong, any help would be appreciated. I updated the xml file it results in the following combbox.
There are a few problems with this code.
One, the binding is set up wrong. Since the property with the viewmodel collection is DriveList, the binding should be ItemsSource="{Binding Path=DriveList}".
Two, you are attempting to display a field from your viewmodel, which is not doable. WPF's binding engine only works with properties, so the viewmodel should have a property:
public string Drive { get; set; }
And finally, the DisplayMemberPath should match the property name from the viewmodel: DisplayMemberPath="Drive".
Update: I just noticed that the DataContext is the observable collection itself -- I probably missed it on the first read. In that case, you want to bind directly to the data context:
ItemsSource="{Binding}"
And set DisplayMemberPath to the property you want to display:
DisplayMemberPath="Drive"
I have an object called "Employee", as follows:
class Employee
{
public string EmpName { get; set; }
public string Surname { get; set; }
public Employee(string Name, string Surname) : base()
{
this.EmpName = EmpName;
this.Surname = Surname;
}
public Employee()
{
}
}
I also have this simple "EmployeeControl" here. Just two labels in a grid:
<UserControl x:Class="LabelBindingTest.EmployeeCtrl"
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"
mc:Ignorable="d"
Name="EmpCtrl"
d:DesignHeight="120" d:DesignWidth="411">
<Grid>
<Label Content="{Binding ElementName=EmpCtrl, Path=EmpName}" Height="28" HorizontalAlignment="Left" Name="label1" VerticalAlignment="Top" Width="81" />
<Label Content="{Binding ElementName=EmpCtrl, Path=Surname}" Height="28" HorizontalAlignment="Right" Name="label2" VerticalAlignment="Top" Width="81" />
</Grid>
</UserControl>
(Edit) EmployeeControl codebehind:
public partial class EmployeeCtrl : UserControl
{
Employee thisEmployee = new Employee();
public string EmpName
{
get
{
return thisEmployee.EmpName;
}
set
{
thisEmployee.EmpName = value;
}
}
public string Surname
{
get
{
return thisEmployee.EmpName;
}
set
{
thisEmployee.EmpName = value;
}
}
public EmployeeCtrl()
{
InitializeComponent();
}
}
Now, what I'm trying to do is add "EmployeeControl"s to my window and bind them to Employee objects.
Here's my "window" code:
<Window x:Class="LabelBindingTest.MainWindow"
Name="myWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525" xmlns:my="clr-namespace:LabelBindingTest">
<Grid>
<my:EmployeeCtrl EmpName="Daniel" Surname="Hammond" HorizontalAlignment="Left" Margin="23,26,0,0" x:Name="employeeCtrl1" VerticalAlignment="Top" Height="97" Width="429" />
</Grid>
</Window>
I've tried a variety of things but I just can't get it to work. It compiles just fine but the label is empty. I just want to attach my "Employee" object to the "EmployeeCtrl" control. Any ideas on how I go around this? I've read a lot about binding today and I'm still scratching my head. I'm not sure if I need to implement INotifyPropertyChanged as I only set the employee names before runtime for now.
(Edit): I've downloaded Caliburn.Micro and used the same code in the last answer. Same result, empty window.
Here's the whole project. All of it's code should be pasted in this question, though. This is just for convenience sake.
http://www.mediafire.com/?gux3573rz64mupe
Ok, firstly I would strongly recommend considering MVVM as your application gets more complicated, and use an MVVM framework if you're using MVVM. I would recommend Caliburn.Micro which makes view composition like you're doing much much easier. There are other MVVM frameworks available, so evaluate them all.
For your issue, the problem is that your user control doesn't implement its properties as dependency properties. In the XAML, you're setting the EmpName and Surname properties of the user control, but the UI doesn't know that their values have changed after the user control is constructed, and therefore doesn't update itself.
You implement INotifyPropetyChanged on view models when using the MVVM pattern, and dependency properties on user controls. Both of these technqiues will notify the UI of a change and force it to update itself.
So for your user control you can do:
public partial class EmployeeCtrl : UserControl
{
public static readonly DependencyProperty EmpNameProperty =
DependencyProperty.Register("EmpName", typeof(string),
typeof(EmployeeCtrl), new FrameworkPropertyMetadata(null));
public static readonly DependencyProperty SurnameProperty =
DependencyProperty.Register("Surname", typeof(string),
typeof(EmployeeCtrl), new FrameworkPropertyMetadata(null));
public EmployeeCtrl()
{
this.InitializeComponent();
}
public string EmpName
{
get { return (string)GetValue(EmpNameProperty); }
set { SetValue(EmpNameProperty, value); }
}
public string Surname
{
get { return (string)GetValue(SurnameProperty); }
set { SetValue(SurnameProperty, value); }
}
}
You don't actually need your Employee class at all at this stage as you aren't using it, but ideally it should implement INotifyPropertyChanged if it's property values will change after construction and you have an instance of it is bound to the UI and you wish the UI to be updated.
you need to implement INotifyPropertyChanged on your EmployeeCtrl's EmpName and Surname property as the Label in this user control needs to be updated about the value change in this property value so that label can update its value. Hence you have to implement Notify change on EmpName and Surname property of the EmployeeCtrl or simply use dependency property for this two property. Hope this helps!!!
Im trying to display the name and path of a file in a listbox. For example, the name should come under Header FileName and path under FilePath. I do not want to bind to any xmls, as i have a code to display the file name and size.Im new to this and im not sure how to go about this. Thanks!
I'm not sure how to help you without seeing any of your code or structure of the data you are trying to bind but I'll give it a shot.
Let's say you're trying to bind the names and paths of files in C:\MyFolder directory and your grid view has a name grd_MyGrid:
string[] myFiles = Directory.GetFiles("C:\\MyFolder\\");
var files = from f in myFiles
select new{
FileName =Path.GetFileName(f),
FilePath = Path.GetPathRoot(f)
};
grd_MyGrid.DataSource=files;
In order for this to work, you have to reference System.Linq.
Hope this helps.
To start you off, I will supply some code, but you really should read up on at least some of the basics when it comes to XAML and WPF for future development tasks.
If you can do without the ListBox, I would suggest using a DataGrid (in .Net 4.0 - or in the WPF Toolkit on CodePlex). The DataGrid is easier to use in situations where you want to display data in a grid or report.
To create a DataGrid in XAML, you can use the following code (in .net 4)
<DataGrid HorizontalScrollBarVisibility="Auto" ItemsSource="{Binding Path=ItemsToDisplay}" IsReadOnly="True" AutoGenerateColumns="True" />
This will create a simple DataGrid object for you to display on screen. Because AutoGenerateColumns has been set to true, your columns will be created for you automatically by the control.
You may also notice that I have set the ItemsSource property, this is the property that the DataGrid will get it's items from.
To define this in the Page code-behind file, you will be able to do something like this:
public System.Collections.ObjectModel.ObservableCollection<Item> ItemsToDisplay { get; private set; }
Notice how the name of the property in the code-behind matches the name of the property in the Binding on the DataGrid. This is how the View (page file) links to the ViewModel (code-behind)
To test it out, create a simple test class and populate the ItemsToDisplay collection with items.
For example:
In my MainWindow.xaml.cs file
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
DataContext = this;
ItemsToDisplay = new System.Collections.ObjectModel.ObservableCollection<Item>();
ItemsToDisplay.Add(new Item("Homer", 45));
ItemsToDisplay.Add(new Item("Marge", 42));
ItemsToDisplay.Add(new Item("Bart", 10));
ItemsToDisplay.Add(new Item("Lisa", 8));
ItemsToDisplay.Add(new Item("Maggie", 2));
}
public System.Collections.ObjectModel.ObservableCollection<Item> ItemsToDisplay { get; private set; }
}
public class Item
{
public string Name { get; private set; }
public int Age { get; private set; }
public Item(string name, int age)
{
Name = name;
Age = age;
}
}
And in my MainWindow.xaml file:
<Window x:Class="Stackoverflow.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>
<DataGrid HorizontalScrollBarVisibility="Auto" ItemsSource="{Binding Path=ItemsToDisplay}" AutoGenerateColumns="True" IsReadOnly="True" />
</Grid>
</Window>
Which looks like: