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:
Related
Edit: The marked solution is correct, however if anyone is having similar problems using SQLite avoid calling the database using using as shown in my example as it messes up lazy loading as the context is closed before it can lazy load. To fix this create a new Object as that will cause lazy loading to fully copy the object passed back from the db so either do that in "using" or manually close the db after you have copied.
I have made a simple example to describe the problem
<Grid>
<StackPanel>
<ListView x:Name="People" ItemsSource="{Binding}">
<ListView.ItemTemplate>
<DataTemplate>
<StackPanel>
<TextBlock Text="{Binding Name}"/>
<ListView x:Name="PhoneNumbers" ItemsSource="{Binding PhoneNumbers}"/>
</StackPanel>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</StackPanel>
</Grid>
So I have a ListView bound to an object let's call it Person() this object has a property of type collection that I want to access. I want to bind a list property in this case just a list of strings to x:name="PhoneNumber" how can I do this? I'm setting the ItemSource of the parent ListView in the code behind. I would like to know any information you have on this topic as I have not been able to find a whole lot on the subject read all of the c# docs, searched stack most I was able to find was to use an ObservableList which I tried, it didn't work. Looking for a data dump any information you think could get me moving in the right dirrection would be greatly appreciated as my actual use case is much more complicated than this example.
I'm aware this would be easier using MVVM but for now I want to get a basic prototype up and running without getting in to MVVM. The end goal is to move to MVVM.
Edit : Literally just binding in code behind atm
Simple example VV
public LoadingWindow()
{
InitializeComponent();
using (var db = new PeopleContext())
{
var Test = db.People.ToList();
if (Test != null)
{
People.ItemsSource = Test;
}
}
}
Example Class
public class People
{
public string Name { get; set; } = "";
// I did read that using an observable list was needed but I tried it and it didn't work
// I'd prefer not to use an observable list
public List<string> PhoneNumbers { get; set; } = new List<string>();
}
Probably the question is not clear, but I figured it out. First of all, you should bind your ListView to ObservableCollection or BindingList in order to get UI updated when your collections are changed. The second problem is that you are not using property to bind your list, you are trying to set your ListView's ItemsSource property to the field, which is not possible. You are using this code var Test = DatabaseInfo.ToList(); Test is a field and then you are trying to bind to it, it is clear that it won't work. You should add a property to your MainWindow BindingList<Person>, and then in the MainWindow constructor fill it with information from DataBase.
So I just created a simple project, as I don't have DataBase I filled collections manually.
In MainWindow I have property BindingList<Person>
public partial class MainWindow : Window
{
public BindingList<Person> People { get; set; }
public MainWindow()
{
InitializeComponent();
People = new BindingList<Person>();
Person person = new Person() { Name = "Jonh" };
Person person2 = new Person() { Name = "Mike" };
People.Add(person);
People.Add(person2);
PeopleList.ItemsSource = People;
}
}
Person class is defined like this
public class Person
{
public BindingList<string> PhoneNumbers { get; set; }
public string Name { get; set; }
public Person()
{
PhoneNumbers = new BindingList<string>();
PhoneNumbers.Add("1");
PhoneNumbers.Add("2");
PhoneNumbers.Add("3");
PhoneNumbers.Add("4");
}
}
I used same XAML, your XAML code doesn't have any problems
<ListView x:Name="PeopleList">
<ListView.ItemTemplate>
<DataTemplate>
<StackPanel>
<TextBlock Text="{Binding Name}"/>
<ListView x:Name="PhoneNumbers" ItemsSource="{Binding PhoneNumbers}"/>
</StackPanel>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
Here is result
Just use BindinList<T> or ObservableCollection<T> and it should work.
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.
Im trying to write a simple WPF app with MVVM pattern, but showing elements of a list does not work I ame pretty sure that something is wrong with binding because its my first time with it
<Window.Resources>
<local:ViewModel x:Key="test"/>
</Window.Resources>
<Grid>
<ListView Name="lstPersons" ItemsSource="{Binding test.peopleList}" >
<ListView.View>
<GridView.Columns>
<GridViewColumn Header="name" DisplayMemberBinding="{Binding name}" />
<GridViewColumn Header="surname" DisplayMemberBinding="{Binding surname}" />
View Model fragment:
public class ViewModel
{
private personModel.Root peopleDB = new personModel.Root();
public ViewModel()
{ }
public List<personModel.Person> peopleList
{
get { return peopleDB.people; }
}
Model class fragment:
public class Root
{
public List<Person> people;
public Root()
{
people = new List<Person>();
people.Add(new Person("aa", "aa", 1, new Adress("bb", "cc")));
people.Add(new Person("bb", "bb", 1, new Adress("bb", "cc")));
people.Add(new Person("cc", "cc", 1, new Adress("bb", "cc")));
}
}
public class Person
{
public string name { get; set; }
public string surname { get; set; }
public int age { get; set; }
public Adress address { get; set; }
tried couple of things with binding but none of them worked :/
The problem here sounds like your DataContext is not set.
There's multiple ways of doing that. As escull638 said, you could manually hardcode the DataContext in with the Window using either XAML
<Window.DataContext>
<local:ViewModel>
</Window.DataContext>
or Code-Behind
this.DataContext = new ViewModel();
and change your binding now that the .DataContext is set correctly
<ListView ItemsSource="{Binding peopleList}">
But keep in mind that hardcoding the .DataContext like this is typically only be used at the highest level in your application, and it should not be something common to see when working with WPF. Controls in WPF are intentially "lookless", and the binding system is used to pass them their data, so by doing something like hardcoding the DataContext means you cannot use the control with any other data object, which kind of defeats one of the biggest advantages of using WPF.
Another solution would be to change the Source property of your binding so it points to the static object defined in <Window.Resources>
<ListView ItemsSource="{Binding Source={StaticResource test}, Path=peopleList}">
I prefer this way because it's obvious just looking at the ListView XAML that you are binding to a static source, and it saves all kind of headaches later when you're trying to pass a dynamic source into the Control and discovering the DataContext isn't set to what you expect it.
As a side note, if you're having trouble understanding what the DataContext is for or how it works, I tend to link beginners to this answer of mine which explains it in more detail :)
Set the DataContext to the viewmodel by adding this to your xaml file:
<Window.DataContext>
<local:ViewModel>
</Window.DataContext>
Then when you need to bind something you can just use:
<ListView Name="lstPersons" ItemsSource="{Binding peopleList}" >
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.
The files I have created and will be referring to in this question are:
TechnicainSelectionView.xaml
TechnicianSelectionView.cs
TechnicianSelectionViewModel.cs
Technician.cs (Code First Entity)
I have the following xaml in my TechnicanSelectionView.xaml
<UserControl xmlns etc... here"
d:DesignHeight="48" d:DesignWidth="300">
<Grid>
<StackPanel>
<Label Content="Select a Technican to run the test" FontWeight="Bold"></Label>
<ComboBox ItemsSource="{Binding Technicians, Mode=TwoWay}"></ComboBox>
</StackPanel>
</Grid>
</UserControl>
The Technicians property to which the ItemSource is set to bind to states that it Cannot resolve Technicians due to an unknown DataContext.
So if we look to my TechnicianSelectionView.cs code-behind...
public partial class TechnicianSelectionView : UserControl
{
public TechnicianSelectionViewModel ViewModel { get; private set; }
public TechnicianSelectionView()
{
InitializeComponent();
Technician.GenerateSeedData();
ViewModel = new TechnicianSelectionViewModel();
DataContext = ViewModel;
}
}
... we see that I am setting the view's DataContext to my TechnicianSelectionViewModel ...
public class TechnicianSelectionViewModel : ViewModelBase
{
public ObservableCollection<Technician> Technicians { get; set; }
public TechnicianSelectionViewModel()
{
Technicians = new ObservableCollection<Technician>();
}
public bool IsLoaded { get; private set; }
public void LoadTechnicians()
{
List<Technician> technicians;
using (var db = new TestContext())
{
var query = from tech in db.Technicians
select tech;
foreach (var technician in query)
{
Technicians.Add(technician);
}
}
IsLoaded = true;
}
}
Techicians is a property on my ViewModel...
So having already set the DataContext for the view, why can't it resolve Technicians on the ViewModel as the DataContext/property it is going to bind to?
EDIT:
As per a concern in a comment below. This is a design time problem and not compile time. I should have indicated this at the start.
You need to specify the type of data context in the xaml to get design-time support. Even though you assigned the data context in code-behind, the designer is not going to recognize that.
Try putting the following in your xaml:
d:DataContext="{d:DesignInstance vm:TechnicianSelectionViewModel}"
See this link for more details.
In my Xamarin Forms Xaml file I used the following lines in the header (ContentPage tag) and it worked perfectly as I wanted.
Basically now
the intellisense shows the fields in the binding
my Resharper is able to rename the binding in the Xaml file if I refactor the name of the property
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:vm="clr-namespace:YourApplicationName.ViewModels;assembly=YourApplicationName"
mc:Ignorable="d"
d:DataContext="{d:DesignInstance {x:Type vm:CurrentPageViewModel}}"