How can I get multiple instances of a usercontrol without the viewmodel being shared? Each usercontrol (and thus viewmodel) should be an instance of its own.
I have read a solution in this question: MVVMLight UserControl View Model-Create new Instance of User control for each view but I cannot get it to work!
I have a listview and a tabcontrol. When I click an item of the listview a new tab must be created with as content the usercontrol which holds information from the selected listitem. The problem is that when selecting multiple items all the items contain the information from the last selected item.
This is my viewmodellocator:
public DossierDetailViewModel DossierDetail
{
get
{
return new DossierDetailViewModel();
}
}
And I call the new usercontrol like this:
DossierDetailViewModel newDossier = new DossierDetailViewModel();
newDossier.TabName = SelectedDossier.Omschrijving;
this.OpenDossiers.Add(newDossier);
Messenger.Default.Send<DTO.Dossier.Dossier>(SelectedDossier, "SetDossier");
EDIT:
Is there no one who can help me or put me in the right direction? :(
The answer of this problem can be found here: https://mvvmlight.codeplex.com/discussions/577555
Related
I have a page with two parts :
A Listview with itemsource binded with a class, ObservableCollection "Patients" loaded with "Patient" class.
Under the Listview are Textboxes binded with the selecteditem Patient. Everything works without writing any code in the page, except in selection_changed to scroll to selected item.
A second Listview must display the details "Visites" from the selected "Patient".
The application works in a MVVM Framework with a Viewmodel containing the properties for the page.
The problem is to make the relation between the two ListView. I tried first building the second list "Visites" in the NotifyPropertyChanged event :
if (Patient.ID > 0)
{
LoadVisite(Patient.ID); // fill the details list "Visites"
NotifyPropertyChanged("Visites");
}
No delail is shown when "Patient" is selected.
I tried another solution inserting the list of details in the master class "Patient like this :
public Class Patient
...
public ObservableCollection<ClsVisite> Visites
{
get
{
return _visites;
}
set
{
_visites = value;
}
}
// WDABase class to open the database and load data connection
WDABase wd = new WDABase();
wd.LoadListeVisites(ID, _visites); //ID is the relation beween the two tables
}
}
Now I try to create the Listview detail itemsource in the XAML like this :
<ListView Name="ListeVisites" ItemsSource="{Binding Path=Patient.Visites}" SelectedItem="{Binding Visite}">
No details where shown.
The unique solution I found was to add some code in the selection_changed event of the master Listview like this (in this case the listviews are in two different frame) :
private void ListePatients_SelectionChanged(object sender, System.Windows.Controls.SelectionChangedEventArgs e)
{
ListePatients.ScrollIntoView(ListePatients.SelectedItem);
if(ListePatients.SelectedItem != null)
{
var w1 = ((System.Windows.FrameworkElement)App.Current.MainWindow.Content).Parent;
Frame pageVisite = (w1 as MainWindow).Visit;
var w2 = (System.Windows.FrameworkElement)pageVisite.Content;
ListView Lv = (w2 as Visite).ListeVisites;
Lv.ItemsSource = (ListePatients.SelectedItem as ClsPatient).Visites;
}
}
And it's works but is there another elegant solution to bind the itemsource of the details Listview ?
Thanks for helping me.
Jean-Marie
I found the solution to my problem.
The Reason the details were not displayed is because the frame used for the listview is not common with the master frame.
I discover that when I display the details frame the "Patient" object contains only null. I Don't know why but I read about sharing ViewModel and now I use a common one in App.xaml and I Don't need to specify the itemsource for the details Listview in code. I put in XAML itemsource={Binding Patient.Visites} and everything works.
This is my first attempt at MVVM. My application's core is loosely based Josh Smith's msdn article. And I am also using the mvvm light framework.
I have a main window containing a command list area and a workspace area which shows usercontrols/views as tabitems, each usercontrol has a corresponding viewmodel. The mainWindow also has a viewmodel containing my command list, and the workspace viewmodels have a base workspace viewmodel.
My default view has a master datagrid, of MappingSets, that can have one selected item. The commands launch new tabitems with views that handle MappingSet detail based on that selected item. I have a View/ViewModel that, depending on the command used should return either a tabitem for creating a new MappingSet with no existing data, or a tabitem containing the detail of the selected item for editing, or a tabitem containing detail the selected item as the base for a new MappingSet.
Having Set the scene, what I have not managed to work out is command dependent way to pass parameters, such as the identifier of the selected MappingSet object, to instantiate my viewmodel in one of the three states mentioned above? For instance would the mvvmlight messenger be appropriate for this task?
This is a perfect scenario for the messenger/eventaggregator. However, your message chain might be a bit convoluted. From what I'm understanding, your Main window holds a list of commands (like a menu or a ribbon). Here is how I see the chain of events.
You select a MappingSet from the datagrid, this causes a MappingSetSelected message to be fired (with a payload of the selected MappingSet)
The main window listens for that message and stores the currently selected MappingSet
When the user clicks the button a "EditMappingSet" or "CreateNewMappingSet" message is fired (or if the Window is responsible for creating the new views, it creates them itself).
If there are only three options, you could have them binding to three different commands and within the commands do the passing of your self-defined variable.
private RelayCommand _openMappingSetCommand;
//Command that one of your options is bound to
public ICommand ViewMappingSetOption1
{
get
{
if (_openMappingSetCommand == null)
{
_openMappingSetCommand = new RelayCommand(param => this.DoTabRequest("your parameter");
}
return _openMappingSetCommand ;
}
}
// Method that creates your viewmodel
private void DoTabRequest(parameterType parameter)
{
WorkspaceViewModel viewModel = null;
if (viewModel == null)
{
viewModel = (WorkspaceViewModel)Activator.CreateInstance(typeof (viewModelType), parameter);
this.Workspaces.Add(viewModel);
}
this.ActiveWorkspace = viewModel;
}
Then allow for that parameter on the constructor of your viewmodel and do whatever else you need based on that.
I have Window that has a ListBox
ListBox(MyListBox) has a DataTable for its DataContext
ListBox's ItemSource is : {Binding}
Listbox has a UserControl(MyUserControl) as DataTemplate
UserControl has RadioButtons and TextBoxes (At first They're filled with values from DataTable and then user can change them)
Window has one Submit Button
What I want to do is, when user clicks the submit button
For each ListBox Item, get the values form UserControl's TextBoxes and RadioButtons.
I was using that method for this job :
foreach(var element in MyListBox.Items)
{
var border = MyListBox.ItemContainerGenerator.ContainerFromItem(element)as FrameworkElement;
MyUserControl currentControl = VisualTreeHelper.GetChild(VisualTreeHelper.GetChild(VisualTreeHelper.GetChild(myBorder,0) as Border,0)as ContentPresenter,0)as MyUserControl;
//And use currentControl
}
I realised nothing when using 3-5 items in Listbox. But when I used much more items, I saw that "var border" gets "null" after some elements looped in foreach function.
I found the reason here :
ListView.ItemContainerGenerator.ContainerFromItem(item) return null after 20 items
So what can I do now? I want to access all items and get their values sitting on user controls.
Thanks
You should use objects who implement INotifyPropertyChanged and bind an ObservableCollection of it to the ItemSource
And then you can get all the list of items.
Here some quick links from MSDN to get more informations
How to: Implement Property Change Notification
Binding Sources Overview
You should google for some tutorials about this.
Zied's post is a solution for this problem. But I did the following for my project:
I realised that there's no need to use UserControl as DataTemplate in my project. So I removed ListBox's DataTemplate.
I removed MyListBox.DataContext = myDataTable and used this:
foreach(DataRow dr in myDataTable.Rows)
{
MyUserControl muc = new MyUserControl(dr);
myListBox.Items.Add(muc);
}
I took DataRow in my UserControl's constructor and did what I want.
And at last I could access my UserControls in ListBox by using :
foreach(MyUserControl muc in
myListBox)
{
//do what you want
}
Easy huh? :)
I'm just getting started with silverlight.
Basically I have a silverlight user control that has various dataGrids and a combobox, their item sources set to the properties of a custom plain c# object.
My problem is that I have a dropdown list that when a user selects an item from the list a new row should appear in one of the grids.
All I'm doing is handling the SelectionChanged event and adding a new item to to a list in my custom object and setting the itemsource for the grid again. This doesnt seem to work; no row is added to the dataGrid
I have no idea how to force my grid to "rebind" to this property.
I've been reading about dependency properties, are these what I need?
Any pointers would be really appreciated.
The list you are binding against should be of the type ObservableCollection. Then the datagrid should display the new item automatically .
The problem is that when you assign the same List to the ItemsSource the DataGrid knows its the same List so it does nothing.
As Henrik points out you should expose an Observable<T> not a List<T> for properties that are to be bound to ItemsSource properties of multi-item controls such as DataGrid, ListBox etc.
In addition your "plain c# objects" should implement the INotifyPropertyChanged interface if you want changes made by code to these properties to automatically appear in the UI.
What you probably want to do is update the binding source - which is relatively easily done.
private void ComboBox_SelectionChanged(object sender, RoutedEventArgs e)
{
this.dataGrid.GetBindingExpression(DataGrid.ItemsSource).UpdateSource();
}
This is a tad hack-y but will do what you need it to do. Implementing INotifyPropertyChanged is another great suggestion.
Silverlight show have some great info on INotifyPropertyChanged here
I have a whole bunch of code that is dependent on the ComboBox type, and I want to be able to use it on a new UI, but the UI that I want to use it for needs to look like a RadioButton for usability reasons. Is it possible to have a ComboBox control look like a set of RadioButtons?
My suggestion would be to use an ItemsControl with a DataTemplate that would render RadioButtons. To the ItemsControl you'd bind the same thing you're binding to the ComboBox.
One caveat is that you need to set the GroupName of the radio buttons to something that would be the same to the group, so they can be mutually exclusive. Otherwise, if you don't do anything, you'll be able to select more than one RadioButton simultaneously.
You could build a new UserControl that has many of the same methods that the ComboBox class does, but adapt it so that it creates multiple radio boxes instead.
Your question is kinda vague though.
IE create an Items collection on your user control, and when something is added, draw a radio box and resize your control, instead of what a combo box does and just adds a string to the list.
Then all you have to do is find and replace all your references to ComboBox with RadioIFiedComboBox.
Heres some comparison:
ComboBox cb = new ComboBox();
cb.Items.Add("blah");
or
RadioIFiedComboBox cb = new RadioIFiedComboBox();
cb.Items.Add("blah");
and
public class RadioIFiedComboBox : UserControl {
public ObservableCollection<object> Items = new ObservableCollection<object>();
public RadioIFiedComboBox() {
Items.CollectionChanged += new NotifyCollectionChangedEventHandler(YourCollectionChanged);
}
private void YourCollectionChanged(){
//do something here to redraw your controls
}
}
The code above is just an example, you'd have to create all the methods you use in the ComboBox class and create similar functionality.