How can i bind a listbox for exemple to a method which is in another project like this :
Project1(BDD)
Class1
Project2(GUI)
class2
I want to bind a listbox itemSource in the second project(GUI), with a class created in my first project(BDD).
How can i bind a listbox for exemple to a method which is in another project?
Put simply, we don't do that in WPF. Instead, you should add a reference to the other project, add a using declaration and then instantiate an object from that class. Then (assuming that your method returns a collection of some sort) you should set the output of the method to a collection property that you then data bind to the ItemsSource property.
Here's a very basic example:
private ObservableCollection<int> numbers = new ObservableCollection<int>();
public ObservableCollection<int> Numbers
{
get { return numbers; }
set { numbers = value; NotifyPropertyChanged("Numbers"); }
}
...
Numbers = new YourClassFromOtherProject().GetData();
...
<ItemsControl ItemsSource="{Binding Numbers}" ... />
Related
This could probably be solved from different angles, but I'm kind of stuck right now.
I want to have a Datagrid, which can add Data of different types into lists in the backend.
I have a list of ObservableCollections, which all have different types of elements in it (I'm using MVVM and I have a couple of different model classes). In my frontend, I have a DataGrid, which is supposed to display the data of the lists depending on which value is selected in a Combobox. It also has to happen dynamicly and can't use fixed grids, as things are added and removed constantly.
I got the Binding to work, so it displays the lists if there are any entries by making the lists the type ObservableCollection.
However, if they are empty, the column headers will disappear and thus, the user cannot enter a new row of the correct type, which is necessary.
This is what I use right now to initiate the Collections:
foreach (var lvm in ListOfValues)
{
Type listType = lvm.GetListType();
string lvmName = listType.Name;
// Create ObservableCollection
ObservableCollection<object> observableCollection = new ObservableCollection<object>
{
// add a basic line to signal the list type - THIS IS A WORKAROUND
Activator.CreateInstance(listType),
};
TemplateEntries.Add(new TemplateEntry { ListName = lvmName, Values = observableCollection });
}
Each TemplateEntry from the class contains the before named list of data. The TemplateEntries Property is just an ObservableCollection of TemplateEntries:
public ObservableCollection<TemplateEntry> TemplateEntries { get; set; } = new ObservableCollection<TemplateEntry>();
public class TemplateEntry : ObservableObject
{
public string ListName { get; set; }
public ObservableCollection<object> Values { get; set; }
public int Count { get { return Values.Count; } }
}
And in XAML I just have this line so far to display it:
<DataGrid Grid.Column="0" Grid.Row="1" Margin="5,5,5,5" AutoGenerateColumns="True" Name="Grid" CanUserAddRows="True" ItemsSource="{Binding ValuesToDisplay}" />
So my question: How can I remove the Activator.CreateInstance Line workaround and signal the datagrid which type it should use?
After some time, I managed to figure it out myself. The solution was to use the non-generic type IList instead of ObservableCollection for the Values of the TemplateEntry class, then cast the ObservableCollection into it (after creating it using Reflection). The DataGrid will accept this.
foreach (var lvm in ListModel.ListVMs)
{
Type listType = lvm.GetListType();
string lvmName = listType.Name;
Type collectionType = typeof(ObservableCollection<>).MakeGenericType(listType);
// Create ObservableCollection
IList observableCollection = (IList)Activator.CreateInstance(collectionType);
TemplateEntries.Add(new TemplateEntry { ListName = lvmName, Values = observableCollection });
}
First of all, what I'm trying to do is a "simple" binding of a ComboBox to my source.
The structure is something like:
public class Data
{
public ObservableList<string> List {get;set;}
public string Selected {get;set;}
}
Also, it implements INotifyPropertyChanged interface.
My problem is, i found several solutions to do this via XAML, unfortunately i can't do it with XAML since my ComboBoxes have to be generated during runtime.
So my question is, how i can bind my ComboBox to Data.List, and also the selected item (value?) to Data.Selected, and this one should be TwoWay so my Data class knows that something was selected. Keep in mind this has to be through c# code (XAML is no option unfortunately).
Thanks in advance. :)
It's pretty easy. Assuming, that Data has properties instead of fields:
public class Data
{
public Data()
{
List = new ObservableCollection<string>
{
"Apple", "Orange", "Lime"
};
}
public ObservableCollection<string> List { get; private set; }
public string Selected { get; set; }
}
you can write this:
var comboBox = new ComboBox
{
DataContext = new Data()
};
comboBox.SetBinding(ComboBox.ItemsSourceProperty, new Binding("List"));
comboBox.SetBinding(ComboBox.SelectedItemProperty, new Binding("Selected")
{
Mode = BindingMode.TwoWay
});
To add ComboBox into visual tree, just call proper method for the container. E.g., this will work with any ContentControl (like Window):
AddChild(comboBox);
how i can bind my combobox to Data.List, and also the selected item (value?)
Create a custom composite user control which contains the combobox. Map the combobox's properties to two dependencies properties created on the custom control, one to load the data and the other to provide an on demand selected item's data. Any plumbing needs are done inside the codebehind which ultimately provides all the magic.
Then you can create/bind this control dynamically in codebehind as needed in the other page you are working on.
Sounds like a sort of "recursive binding". If your combos are in a container control, what you need is bound the container to a collection of your single combo model, so each view in the ItemsControl will be bound to a single combo model.
Here's the deal: I have to take a SelectedItem from a Listbox, that I got from this question and add it to a ListBox in another UserControl. The ViewModels and models are all setup, I just need to know how to refer to the ListBox that is getting the items.
This would be under ViewModel A -- the ViewModel that controls the user control with the ListBox that receives the items.
//This is located in ViewModelA
private void buttonClick_Command()
{
//ListBoxA.Items.Add(ViewModelB -> SelectedListItem);
}
I don't understand how to get ListBoxA.
Would it be an ObservableCollection of strings?
For further clarification: ListBoxA, controlled by ViewModelA will be receiving values from ListBoxB in ViewModelB. I have included a property for ViewModelB in ViewModelA
You need to have a property in ViewModelA that can be any type that implements IEnumerable. I will use a list:
public const string MyListPropertyName = "MyList";
private List<string> _myList;
/// <summary>
/// Sets and gets the MyList property.
/// Changes to that property's value raise the PropertyChanged event.
/// </summary>
public List<string> MyList
{
get
{
return _myList;
}
set
{
if (_myList == value)
{
return;
}
RaisePropertyChanging(MyListPropertyName);
_myList = value;
RaisePropertyChanged(MyListPropertyName);
}
}
Then in your Listbox, you need to set the ItemsSource to this list
<ListBox ItemsSource="{Binding MyList}">
.......
</ListBox>
Now in your constructer, fill MyList with the data you want to display, and on the Add Command, you want to put
MyList.Add(ViewModelB.myString);
ViewModelB.myString assuming from your previous question that in ViewModelB you have a property myString bound to the SelectedItem of ListBoxB, and you have a reference to the instance of ViewModelB in ViewModelA.
This should do it, let me know
Update:
You should be using an ObservableCollection in VMA since the collection will be added to.
I have a ComboBox with few static values.
<ComboBox Name="cmbBoxField" Grid.Column="4" Grid.Row="2" Style="{StaticResource comboBoxStyleFixedWidth}" ItemsSource="{Binding}" ></ComboBox>
MVVMModle1.cmbBoxField.Items.Add(new CustomComboBoxItem("Text Box", "0"));
MVVMModle1.cmbBoxFieldType.Items.Add(new CustomComboBoxItem("Pick List", "1"));
MVVMModle1.cmbBoxFieldType.Items.Add(new CustomComboBoxItem("Check Box", "2"));
MVVMModle1.cmbBoxFieldType.Items.Add(new CustomComboBoxItem("Radio Button", "3"));
When I am saving the data in Database table it is getting saved.
((CustomComboBoxItem)(MVVMModle1.cmbBoxField.SelectedValue)).Value.ToString();
Now when I am trying to Edit my form and binding the value again to combobox it is not showing the value.
MVVMModle1.cmbBoxField.SelectedValue = dtDataList.Rows[0]["ControlList"].ToString().Trim();
Someone please help me in this. How to bind selected value to the combobox?
There are quite a few problems with your code here:
You are setting the ItemsControl.ItemsSource property to the default binding (bind to the current data context), which is incorrect unless the DataContext is any type that implements IEnumerable, which it probably isn't.
If this is correct because the DataContext is, for example, an ObservableCollection<T>, then you still have an issue because you are adding items statically to the ComboBox instead of whatever the ItemsSource is.
Also, the type of items you are adding are CustomComboBoxItem, which I'm going to assume inherits from ComboBoxItem. Either way, you can't say the SelectedValue is some string since the values in the ComboBox are not strings.
You should really not have a collection of CustomComboBoxItem's, but instead a custom class that is in itself it's own ViewModel.
Now that that's been said, here is a suggested solution to your problem:
<ComboBox ItemsSource="{Binding Path=MyCollection}"
SelectedValue="{Binding Path=MySelectedString}"
SelectedValuePath="StringProp" />
public class CustomComboBoxItem : ComboBoxItem
{
// Not sure what the property name is...
public string StringProp { get; set; }
...
}
// I'm assuming you don't have a separate ViewModel class and you're using
// the actual window/page as your ViewModel (which you shouldn't do...)
public class MyWPFWindow : Window, INotifyPropertyChanged
{
public MyWPFWindow()
{
MyCollection = new ObservableCollection<CustomComboBoxItem>();
// Add values somewhere in code, doesn't have to be here...
MyCollection.Add(new CustomComboBoxItem("Text Box", "0"));
etc ...
InitializeComponent();
}
public ObservableCollection<CustomComboBoxItem> MyCollection
{
get;
private set;
}
private string _mySelectedString;
public string MySelectedString
{
get { return _mySelectedString; }
set
{
if (String.Equals(value, _mySelectedString)) return;
_mySelectedString = value;
RaisePropertyChanged("MySelectedString");
}
}
public void GetStringFromDb()
{
// ...
MySelectedString = dtDataList.Rows[0]["ControlList"].ToString().Trim();
}
}
You could alternatively not implement INotifyPropertyChanged and use a DependencyProperty for your MySelectedString property, but using INPC is the preferred way. Anyways, that should give you enough information to know which direction to head in...
TL;DR;
Take advantage of binding to an ObservableCollection<T> (create a property for this).
Add your items (CustomComboBoxItems) to the ObservableCollection<T>.
Bind the ItemsSource to the new collection property you created.
Bind the SelectedValue to some string property you create (take advantage of INPC).
Set the SelectedValuePath to the path of the string property name of your CustomComboBoxItem.
Can you use cmbBoxField.DataBoundItem()? If not target the source from the selected value, i.e. Get the ID then query the source again to get the data.
(CustomComboBoxItem)MVVMModle1.cmbBoxField.DataBoundItem();
When you bind a datasource it is simpler to do it like this:
private List GetItems(){
List<CustomComboBoxItem> items = new List<CustomComboBoxItem>();
items.Add(new CustomComboBoxItem() {Prop1 = "Text Box", Prop2 = "0"});
//...and so on
return items;
}
Then in your main code:
List<CustomComboBoxItem> items = this.GetItems();
MVVMModle1.cmbBoxField.DisplayMember = Prop1;
MVVMModle1.cmbBoxField.ValueMember = Prop2;
MVVMModle1.cmbBoxField.DataSource = items;
This will then allow your selected value to work, either select by index, value or text
var selected = dtDataList.Rows[0]["ControlList"].ToString().Trim();
MVVMModle1.cmbBoxField.SelectedValue = selected;
Ive got two DataGrids. EmployeeGrid and WorkSessionsGrid. Each Employee has a list of WorkSessions that I want the user to access by selecting an Item in the EmployeeGrid which should make the WorkSessionsGrid generate the WorkSessions for the selected Employee. Why is the following not correct?
<DataGrid Name="dg_2" ItemsSource="{Binding ElementName=dg_1, Path=SelectedItem.WorkSessions}"/>
Update
I've come to the conclusion that the problem has to be in one of the other layers.
Here's the remainder of my code, hopefully someone is capable of helping me out.
Is there something fundamentally that I am missing?
Code-Behind xaml
public partial class MainWindow : Window
{
public EmployeeViewModel EmployeeViewModel = new EmployeeViewModel();
public MainWindow()
{
InitializeComponent();
menu_employee.DataContext = EmployeeViewModel;
sp_employee.DataContext = EmployeeViewModel;
datagrid_employees.ItemsSource = EmployeeViewModel.EmployeesView;
sp_worksessions.DataContext = EmployeeViewModel.SelectedEmployee.WorkSessions;
menu_worksession.DataContext = EmployeeViewModel.SelectedEmployee.WorkSessions;
datagrid_worksessions.ItemsSource = EmployeeViewModel.SelectedEmployee.WorkSessions;
}
}
WorkSessionViewModel
class WorkSessionViewModel : ViewModelBase
{
private WorkSessions _workSessionsModel = new WorkSessions();
public WorkSessions WorkSessionsView = new WorkSessions();
private WorkSessionModel _selectedWorkSession = new WorkSessionModel();
public WorkSessionModel SelectedWorkSession
...
WorkSessionModel
[Serializable]
public class WorkSessions : ObservableCollection<WorkSessionModel>
{
public WorkSessions()
{
}
}
[Serializable]
public class WorkSessionModel : INotifyPropertyChanged
{
private DateTime _dateTime;
private string _id;
private double _hours;
public WorkSessionModel()
{
}
Try Binding to the Element in stead.
<DataGrid Content="{Binding ElementName=ListOfEmp, Path=SelectedItem.Name}" DataContext="{Binding}" />
This bit of XAML looks quite correct, try to debug the binding, there might be some other problems like visual tree breaks or the WorkSessions collection perchance is a field and not a property etc.
If there are binding errors please share them.
As #H.B. has pointed out correctly, please use your Visual Studio's Output Window to see any binding errors. They will tell you if the bindings are failing. If you find binding erros then yours binding should be solved for two possible issues...
Source of data is incorrect. Is the data context and items source correctly set for that UI element such as DataGrid?
Path of the property in the binding could be incorrect. Is your SelectedItem having an object has any property named WorkSessions in it? etc.
Apart from this we still dont know what dg_1 and dg_2 from your XAML is. Your code behind shows different names datagrid_employees and datagrid_worksessions I guess.
You should add one more item for the EmployeeViewModel, called: SelectedEmployee and bind that with the employee grid selected item, mode=TwoWay.
Then you databind for the second grid should be:
<DataGrid Name="dg_2" ItemsSource="{Binding Path=SelectedEmployee.WorkSessions}"/>
Since both grids are in the same window, thus, you should only set datacontext for the windows only. In side the viewmodel, you have 2 dependency properties: EmployeeList, SelectedEmployee. Whereas, EmployeeList is binded to ItemsSource of the employee grid. SelectedEmployee is binded to SelectedItem on the employee grid.