TabControl looses child controls - c#

I am currently working on a just for fun solution for myself, to create workout plans for the gym.
Solution on Github
It is a simple WPF solution using Caliburn.Micro to easier get the connections for the MVVM pattern.
On my MainView I have a TabControl:
MainView.xaml:
<TabControl x:Name="Items" />
With the following code for the ViewModel:
public MainViewModel()
{
DisplayName = "Plan2Fit";
Items.Add(new CreatePlanViewModel(_exerciseProviderViewModel));
Items.Add(new ExerciseManagementViewModel(_exerciseProviderViewModel));
}
I only have two ViewModels displayed in the TabControl, one to manage exercises and store them in xml to have kind of a database of your exercises and one where you should later be able to pick exercises for your plan.
Problem:
At startup everything looks ok, but as soon as I switch between the Tabs, one of them might loose its child control for whatever reason.
I have already tried the following:
MainView.xaml:
<TabControl x:Name="Items" cal:Message.Attach="[Event SelectionChanged] = [Action Reload]" />
MainViewModel:
public void Reload()
{
_exerciseProviderViewModel = new ExerciseProviderViewModel();
Items.Refresh();
DisplayName = "Plan2Fit";
}
This makes the error happen less often but it still is existing.
I have already found this question ... the solutions I was able to find are all working with MVVM, but not with Caliburn.Micro, so I am really not havinmg any Idea, how to solve this.
I have tried Avalon dock, but I was not able to get it to work with the Caliburn way of binding x:Name="Items"
Note:
If you want to recreate the Bug using my solution, you have to add some "Exercises" by picking an image in the "Manage Exercise" tab and click Add (You can add the same "Exercise" multiple times).
There is no errorhandling or testing done so far, as this is at the state where I want to validate, if it works at all.

I found the problem, still I dont understand, why it is a problem at all.
Given:
public MainViewModel()
{
DisplayName = "Plan2Fit";
Items.Add(new CreatePlanViewModel(_exerciseProviderViewModel));
Items.Add(new ExerciseManagementViewModel(_exerciseProviderViewModel));
}
The tabcontrol will occasionally loose its childs.
If I pass a new ExerciseProviderViewModel into eacht Items.Add() call, the bug does not occur.
Thus I stored a ExerciseProvider as member and passed this one into my ViewModels I want to add.
public MainViewModel()
{
_exerciseProvider = new ExerciseProvider(new DCSerializer<List<Exercise>>());
DisplayName = "Plan2Fit";
ActivateItem(new CreatePlanViewModel(new ExerciseProviderViewModel(_exerciseProvider)));
ActivateItem(new ExerciseManagementViewModel(new ExerciseProviderViewModel(_exerciseProvider)));
}
This works without any problems.

Related

Loading Multiple ViewModel in a single View Xamarin Forms

I’m working on a Xamarin Forms project, where I have a screen divided into 2 parts, inside a single View (say DashBoardView.Xaml). One part is having some menu options and on the other part I want to load different views on menu option clicked.
The dynamically views having different ViewModels registered (using XLABS MVVM) ViewFactory.
DashBoardView.Xaml
<Grid>
<Grid ColumnSpacing="0" RowSpacing="0" BackgroundColor="#404040">
// menu options having Command binded in DashboardViewModel
</Grid>
<ContentView Grid.Row="0" Grid.Column="1" BackgroundColor="White"
Content="{Binding DashboardDetailView}">
// views are binded with DashboardDetailView
// views are loaded dynamically to DashboardDetailView
(bindable property) using new Keyword when menu option
command is executed.
</ ContentView>
</Grid>
DashBoardViewModel.CS
private Command _dashboardMenuOptionCommand;
public Command DashboardMenuOptionCommand
{
get
{
return _ dashboardMenuOptionCommand?? (_dashboardMenuOptionCommand = new Command(() => {
DashboardDetailView = new ABCView();
}));
}
}
Note: ABCView is a Xaml view registered with ABCViewModel using xLab’s ViewFactory
Problem: The issue is the ABCView is getting loaded but no user interaction is happening, as in no commands binded to a label in ABCViewModel is getting called. Is there something I’m doing wrong or is there any better or optimize way to achieve the same?
Please find the attached screenshot for the reference.
DashboardView.xaml
Instantiating a view won't automatically instantiate a viewmodel and set it as it's binding context.
Most (if not all) MVVM Frameworks will provide you with a method to instantiate or resolve a page, which will automatically look up and hook up the appropriate viewmodel as well. Not too sure about automatically resolving a view with a viewmodel.
If not supported by your framework, you can do it yourself by instantiating your viewmodel, then assigning it to the view's binding context, before adding the view to the page.
Please note that XLabs is a dead project, I'm not too sure it's a smart idea to use it going forward, I suggest you look into current MVVM frameworks (there's a bunch) and pick one that suits your needs and looks good to you.

WPF Data Binding confusion

I've just started taking up a course about WPF and I'm a bit confuse about some areas related to Data-Binding. I have no syntax issue, but most probably committed some newbie errors and I have couple of questions.
I've done a simple screen with 2 textboxes and I when I click a button these two items are added to a ListBox.
Reference within the Window tag of the XAML to the People class
xmlns:classes="clr-namespace:WPF_Course.Classes"
Added a Window resource
<Window.Resources>
<classes:People x:Key="people"/>
</Window.Resources>
Here's how I've declared my Listbox
<ListBox DataContext="{Binding Source={StaticResource people}}"
ItemsSource="{Binding Persons}"
x:Name="PersonListBox">
<ListBox.ItemTemplate>
<DataTemplate>
<Grid>
<StackPanel>
<TextBlock Text="{Binding FullName}"/>
</StackPanel>
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
So, I've added a DataContext to my ListBox where I bind it to my people resource and also I add an ItemSource which looks on a property within my People .
This is my class
public class People : ObservableCollection<Person>
{
public ObservableCollection<Person> Persons { get { return persons; } set { persons = value; } }
private ObservableCollection<Person> persons = new ObservableCollection<Person>();
public People()
{
for (int i = 0; i < 1; i++)
{
// implicitly I add one item just for messing around with the constructor
Persons.Add(new Person()
{
Name = "Dummy",
LastName = "Dummy",
Age = 15
});
}
}
}
Based on what I've done so far I have the following questions :
1) What's the difference between (they have the same effect, but there's more reasoning behind it ? )
ItemsSource = "{Binding Persons}"
and
ItemsSource = "{Binding Path = Persons }"
Also by leaving the ItemSource = "{Binding}" am I actually just instantiating the a People instance, thus all my logic being treated from the constructor of that class ? I've messed around with it and it appears to do so, but I am not sure.
2) On my Peoples class I've Implemented the ObservableCollection<Person> (where Person is also a class). Initially I was doing a static addition to my list from the constructor itself and I had no properties defined within the class ( ObservableCollection<person> type of properties ) thus needing it ( the implementatiton of the interface) but now using a property do I really need it? , so my question is :
If my class's sole purpose is to load things within it's collection from the constructor only ( and not from an outside class, thus needing some sort of a property ), is it a best practice to implement my class with the ObservableCollection<myClass> or to define properties of the same type that I've done ? (for accessing from an outside class)
I am sorry for the weird questions because I know they sound somewhat silly, I'm looking from a validation because I've just started working with wpf recently.
Thanks
Edit 2 : Thank you for all your answers, I understood now. Also I've forgotten to show you how I insert data in my collection. ( Added this edit for me to remember if I forget it and for others that may stumble upon this thread having a similar confusion )
ObservableCollection<Person> ppl;
public MainWindow()
{
InitializeComponent();
person = new Person();
stackPanelPerson.DataContext = person;
people = new People();
ppl = people.Persons;
PersonListBox.ItemsSource = ppl;
}
Initially I was doing like this
ppl.Add(new Person() { Name = boxFirstName.Text, LastName = boxLastName.Text, Age = Int32.Parse(boxAge.Text) });
Then I realized I was using data-binding on my Person Class(INotifyPropertyChanged) with properties so I changed it to :
ppl.Add(new Person() { Name = person.Name, LastName = person.LastName, Age = person.Age});
Thanks again guys for the replies !!
Have a good day !
Question 1:
None, there is no difference. {Binding xyz} is the same as {Binding Path=xyz}, it's almost like a shortcut. But it can only be used on the first thing you write in your binding, for example, you cannot do this:
{Binding ElementName=myElement, xyz}
Instead, you would do this:
{Binding ElementName=myElement, Path=xyz}
Or even better:
{Binding xyz, ElementName=myElement}
Here's a related question.
Question 2:
What you have done is correct, your collection of people should be exposed as a Property, why? Because then you can bind to it.
There is no need for a static property in this scenario.
I would strongly suggest researching the MVVM Design Pattern. You can find a tutorial here
1) Many markup extensions understand shortened syntax, there is no difference between {Binding Persons} and {Binding Path=Persons}. However, sometimes you must use full syntax.
One example would be to make own
public class ExceptionBinding : Binding
{
public ExceptionBinding()
{
ValidationRules.Add(new ExceptionValidationRule());
UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged;
}
}
then you must use full syntax {l:ExceptionBinding Path=Persons}.
2) It depends. You don't have to use ObservableCollection<> if collection is not going to change after you bind to it. Creating List<>, filling it up and then binding to it will work pretty well.
You have to read about MVVM, because using it will simplify usage scenarios and makes many things more clear.
Mike covered what I wanted to say ...
In addition to the binding, you can also show different things in your bindings. Here's a tutorial I've wrote for the code project: Understanding SelectedValue, SelectedValuePath, SelectedItem & DisplayMemberPath + Demo
You can do mockups of your class with dummy data so you'll see a preview in your XAML designer in VS. MVVM light framework helps out and has some cool features as well. There are other frameworks, and you don't really need one for doing MVVM, but they help.
Other than that, I wish you good luck on your journey ... :) once you'll master it, it'll be fun ...
None, there is no difference. {Binding propertyName} is the same as {Binding Path=propertyName}, it's almost like a shortcut, but Constructor get called because of DataContext="{Binding Source={StaticResource people}}".
It depends. You don't have to use ObservableCollection<> if collection is not going to change after you bind to it. Creating List<>, filling it up and then binding to it will work pretty well.But if you want to change collection from screen and update list then you need to go for ObservableCollection<>

Modal Popup and views communication under MVVM pattern on C#

I have a large project coded with VB6 which I've been trying to upgrade to new technologies for a few months. My project consist on 6 administrative modules reunited under a client-server application. Coming from VB, my logical choice was to upgrade to .NET. After a lot of research I decide to use C#, WPF and the MVVM pattern (with Caliburn Micro).
At the beggining I had some problems, but I managed to resolve them. But now I've come to a point where I need (like every complex application) to communicate with different views and their corresponding viewModel through modal popups (or some other technique). And in this matter the MVVM pattern seems to be very restrictive or complex. A simple "Are you sure you want to delete this record (yes/no)" is a very complex task. So I'm looking for advice as how communicate views without complex artifacts as EventAgregators.
So far the only possible alternative I've found is to use the ModalContentPresenter class from this blog. The problems with this solution are:
I need to write the father view XAML and modal XAML on the same view.
I cannot have multiple popus from same view.
Some examples of where I'd like to use modal popups is:
Put a button on a view to select a Client. It should open a popup with all posible clients and let the user chose one.
Add a product popup to a customer order.
Any ideas or suggestions? Some sample code would be appreciated? Thanks!
I am the author of the linked ModalContentPresenter control so I will attempt to address some of your questions and concerns.
I need to write the father view XAML and modal XAML on the same view.
You can actually write both views in separate files. The views can then be loaded dynamically using DataTemplates which will depend on the ViewModel that is bound to either the Content or ModalContent properties.
See this which describes the general way in which this view switching can be achieved.
You could have a MainViewModel which has two properties, PrimaryViewModel and SecondaryViewModel which return appropriate view models which provide the properties and commands for the main and modal content.
You could have the following setup in XAML:
<DataTemplate DataType="{x:Type FooViewModel}">
<Controls:FooView />
</DataTemplate>
<DataTemplate DataType="{x:Type BarViewModel}">
<Controls:BarView />
</DataTemplate>
<controls:ModalContentPresenter
Name="modalPresenter"
Content={Binding DataContext.PrimaryViewModel}
ModalContent={Binding DataContext.SecondaryViewModel} />
When the IsModalproperty is false, only your PrimaryView will be displayed. As soon as you set the IsModal property to true the ModalContentPresenter will display your SecondaryView.
I cannot have multiple popus from same view.
I take it you mean you want to be able to display different modal content at different times from the same main view.
Using the above technique this is as simple as switching the ViewModel that is bound to the ModalContent property (before displaying it by setting IsModal to true). As long as you have a DataTemplate for the ViewModel that is bound (and your MainViewModel implements INotifyPropertyChanged correctly), the correct content will be displayed.
Some example on where i'd like to use modal popups is:
Put a button on a view to select a Client. It should open a popup with
all possible clients and let the user chose one.
Add a product popup to a customer order.
Once you understand the technique described above you should be able to see that the as long as you have a View and ViewModel pair you can cover any scenario you can think of.
As an example, consider viewModels that have the following interfaces:
public interface SelectCustomerViewModel : INotifyPropertyChanged {
event EventHandler CustomerSelected;
public ObservableCollection<Customer> Customers { get; }
public Customer Customer { get; set; }
public Command CustomerSelectedCommand { get; }
}
public interface MainViewModel : INotifyPropertyChanged {
public SelectCustomerViewModel ModalContent { get; }
public Command SelectCustomerCommand { get; }
public bool IsSelectingCustomer { get; }
}
You could have XAML that looks something like this:
<Window x:Class="ModalContentTest.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Select a customer">
<DataContext>
<vm:MainViewModel />
</DataContext>
<DataTemplate DataType="{x:Type SelectCustomerViewModel}">
<Controls:SelectCustomerView />
</DataTemplate>
<c:ModalContentPresenter Name="modalPresenter"
ModalContent={Binding ModalContent}
IsModal={Binding IsSelectingCustomer}>
<!-- This is the primary content! -->
<Grid>
<Button Content="Select a customer"
Command={Binding SelectCustomerCommand} />
</Grid>
</c:ModalContentPresenter>
</Window>
Here's how it works:
The IsSelectingCustomer property of the MainViewModel would start off as false.
Clicking the button in the main view would invoke the SelectCustomerCommand object. The command would then tell the MainViewModel to change the IsSelectingCustomer property to true.
The ModalContentPresenter would display the view specified by the data template. The user can now only interact with the 'select customer view'.
Once a customer has been selected, a button can be clicked (which is bound to the CustomerSelectedCommand of the SelectCustomerViewModel) which in turn would raise the CustomerSelected event.
The MainViewModel would have an event handler that would respond to the CustomerSelected event. The handler would then read the SelectedCustomer property from the SelectCustomerViewModel and finally, it would set the IsSelectingCustomer property back to false, causing the modal content to be closed.

What happens internally when you bind to ItemSource?

I'm curious how this works, because I have a MainViewModel, which has Property say called SubViewModel which has a Property of ObservableCollection (we'll call it Property1.)
I've implemented INotifyChangedProperty on everything.
My Main Window
<Window ..
DataContext="{Binding MainViewModel}" />
...
<StackPanel DataContext="{Binding SubViewModel}">
<local:SomeControl DataContext="{Binding}" />
</StackPanel>
</Window>
And my UserControl
<UserControl Name="SomeControl">
<DataGrid Name="MyDataGrid" ItemSource="{Binding Property1, Mode=TwoWay}" CurrentCellChanged="TestMethod" />
...
</UserControl>
In my test method, just as a test to figure out why the changes are not propegating up to the main view model I do something like this
private void TestMethod()
{
var vm = this.DataContext as SubViewModel;
var itemSourceObservableCollection = MyDataGrid.ItemsSource as ObservableCollection<MyType>;
//I thought vm.Property1 would be equal to itemSourceObservableCollection
//but they are not, itemSourceObservableCollection shows the changes I've made
//vm.Property1 has not reflected any changes made, even though I though they were the same item
}
So I figured out that ItemSource must create a copy of the item you bind it to? I'm stuck here, how do manually notify the viewModel that this property has changed and it needs to update? I thought that was INotifyPropertyChanged's job?
I think part of my problem is I lack the understanding of how this kinda works internally. If anyone can point to a good blog post, or documentation to help me understand why my code isn't working the way I expected, that would be great.
1) No copy is made.
2) ObservableCollection will propogate changes made to the collection, not the items within the collection. So you'll see additions, deletions etc. but NOT property changes to items within the collection.
3) If you want to see changes made to individual items in the ObservableCollection, you need to implement INotifyPropertyChanged on those items.
There's actually TWO different issues here. What happens internally when you bind to a collection? AND why changes on the user surface are not propagated back to your View Model. Based upon what you wrote, the two issues are not connected, but let's take them one at a time...
For the first issue... When you bind a collection, the WPF binding engine creates a "CollectionView" class that mediates between your object store and the logical tree. You can, if needed, get a copy of the the "CollectionView" using a static method on CollectionViewSource...
var cvs = CollectionViewSource.GetDefaultView(MyCollectionOfThings);
There are several interesting properties in the result, and some of them contain write accessors which allow you to directory modify the CollectionView.
For the second issue... The business classes in your SubViewModel need to inherit from INotifyPropertyChanged such that changes are 'announced' via the WPF binding engine. Your VM should be a publisher, but can also be a subscriber. A property that participates in the INotifyPropertyChanged plumbing gets declared like this...
private string _name;
[Description("Name of the driver")]
public string Name
{
[DebuggerStepThrough]
get { return _name; }
[DebuggerStepThrough]
set
{
if (value != _name)
{
_name = value;
OnPropertyChanged("Name");
}
}
}
This code publishes changes, but can also subscribe to changes made on the user surface by setting the appropriate attributes in your Xaml.
Background reading: What is a CollectionView?
Also, Similar question

Silverlight + MVVM + Bindings = Memory leaks?

Thus far, my testing has shown that all standard approaches, examples, and frameworks leveraging the MVVM pattern in silverlight suffer from a huge problem: massive memory leaks which prevent VMs from being garbage collected.
Obviously this is a huge and ridiculous claim - so my expectation is that someone will have an obvious answer of why and where I'm going wrong :)
The steps to reproduce are simple:
Bind your viewmodel to a view by setting the views datacontext to the VM (assume the viewmodel leverages INotifyPropertyChanged to support data binding)
Bind a UI element to a property on the viewmodel, for example:
<TextBox Text="{Binding SomeText}" />
Leverage the binding in some way (for example - just type in the textbox).
This creates a reference chain that extends from the root, to a BindingExpression, to your viewmodel. You can then remove the View from your UI tree, as well as all refernences to the VM - however the VM will never be garbage collected thanks to the root<>BindingExpression<>VM reference chain.
I have created two examples illustrating the problem. They have a button to create a new view/viewmodel (which should dump all references to the old one(s)) and a button which forces garbage collection and reports on the current memory usage.
Example 1 is a super stripped down caliburn micro example. Example 2 uses no frameworks and just illustrates the problem in the simplest way I could think of.
Example 1
Example 2
For those who might like to help but don't wish to download the example projects, here is the code for example 2. We start with a viewmodel called FooViewModel:
public class FooViewModel : INotifyPropertyChanged
{
string _fooText;
public string FooText
{
get { return _fooText; }
set
{
_fooText = value;
NotifyPropertyChanged("FooText");
}
}
private byte[] _data;
public FooViewModel()
{
_data = new byte[10485760]; //use up 10mb of memory
}
private void NotifyPropertyChanged(String info)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(info));
}
}
public event PropertyChangedEventHandler PropertyChanged;
}
It simply exposes a string property called FooText which we will bind too. INotifyPropertyChanged is neccessary to facilitate the binding.
Then we have a view called FooView which is a usercontrol containing:
<UserControl x:Class="MVVMLeak.FooView">
<StackPanel x:Name="LayoutRoot" Orientation="Horizontal">
<TextBlock Text="Bound textbox: " />
<TextBox Text="{Binding FooText}" Width="100"/>
</StackPanel>
</UserControl>
(namespaces omitted for brevity)
The important bit here is the textbox which is bound to the FooText property. Of course we need to set the datacontext, which I've chosen to do in the codebehind rather than introduce a ViewModelLocator:
public partial class FooView : UserControl
{
public FooView()
{
InitializeComponent();
this.DataContext = new FooViewModel();
}
}
MainPage looks like this:
<StackPanel x:Name="LayoutRoot" Background="White">
<Button Click="Button_Click" Content="Click for new FooView"/>
<Button Click="Button2_Click" Content="Click to garbage collect"/>
<ContentControl x:Name="myContent"></ContentControl>
</StackPanel>
with the following in the code behind:
private void Button_Click(object sender, RoutedEventArgs e)
{
myContent.Content = new FooView();
}
private void Button2_Click(object sender, RoutedEventArgs e)
{
MessageBox.Show("Memory in use after collection: " + (GC.GetTotalMemory(true) / 1024 / 1024).ToString() + "MB");
}
Note: To replicate the problem, be sure to type something in the textbox, as I believe the Binding Expression isn't created until it's needed.
It's worth noting that this KB article may be related, however I'm not convinced since 'method 2' workaround doesn't seem to have an effect, and the reference chain doesn't seem to match.
Also, I'm not sure it matters, but I used CLR Profiler to diagnose the cause.
Update:
If anyone would like to test, and report their findings in a comment, I'm hosting the silverlight application via dropbox here: Hosted Example . To reproduce: Hit the top botton, type something, hit the top button, type something, hit the top button. Then hit the button. If it reports 10MB usage (or perhaps some other amount that is not increasing), you are not experiencing the memory leak.
Thus far, the problem seems to be happening on ALL of our development machines, which are ThinkPad w510 (43192RU) with 12GB Ram, 64 bit Win 7 Enterprise. This includes some which have not had development tools installed. It might be worth noting that they are running VMWare workstation.
The problem does NOT seem to happen on other machines I have tried - including several home PCs and other PCs in the office. We have somewhat ruled out SL versions, amount of memory, and probably vmware. Still haven't nailed down a cause.
A solution is yet to be found, however the problem is now identified. This behavior will occur if Silverlights' automation faculties are invoked due to:
Tablet PC Input Service (in other words, all 'tablet like' PCs)
Automated Testing tools
Screen Readers (and other accessability software)
More information here: http://www.wintellect.com/cs/blogs/sloscialo/archive/2011/04/13/silverlight-memory-leaks-and-automationpeers.aspx
So a new problem surfaces: How do we disable automationpeers or otherwise get them to clean-up correctly?
This post illustrates one approach: WPF UserControl Memory leak
However, it isn't really a viable solution as we'd have to override every silverlight control which we plan to use binding for, not to mention the control templates of complex controls.
I will change my answer if anyone can identify a good solution, but for now there doesn't seem to be one...
Edit:
Here is a nice little workaround which seems to do the job. Simply add the following parameter in your HTML where you define the silverlight object:
<param name="windowless" value="true" />
A side-effect of running in 'windowless' mode is that automation doesn't work :)
There is no memory leak in your second example.
After you affect a new FooView instance to your ContentControl using myContent.Content = new FooView();, there is no more used reference to the whole View + ViewModel object graph.
It'll be garbage-collected when needed, eventually.
Maybe you should clarify about what make you think there is a memory leak (i.e. statistics, repro steps...etc.)

Categories

Resources