Please note that this question is specific to SimpleMVVM and the use of its ViewModelLocator.
I have a view setup like so:
<UserControl x:Class="CallTracker.WPF.Views.CallUserControl"
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"
DataContext="{Binding Source={StaticResource Locator}, Path=CallViewModel}">
<StackPanel>
<TextBlock Text="{Binding GreetingText}" />
</StackPanel>
</UserControl>
And my locator defines the CallViewModel as:
// Create CallViewModel on demand
public CallViewModel CallViewModel
{
get
{
return _CallViewModel;
}
set
{
if (_CallViewModel != value)
{
_CallViewModel = value;
}
}
}
private CallViewModel _CallViewModel;
During execution I am trying to create a new CallUserControl to use but, using Snoops, I can see that the datacontext of CallUserControl is not being bound to the viewmodel (the datacontext in Snoops says null).
Stepping thru the code I can see that the CallViewModel is getting created and assigned to the object within the locator but the UserControl's datacontext is not binding to this newly created object.
I am creating the new CallViewModel as such:
private void NewCall()
{
MessageBox.Show("NewCallCommand executed.");
// Only start a new call if one doesn't currently exist
if((App.Current.Resources["Locator"] as ViewModelLocator).CallViewModel == null)
{
CurrentCallViewModel = new CallViewModel();
}
}
....
/// <summary>
/// Current MainContentViewModel
/// </summary>
public CallViewModel CurrentCallViewModel
{
get
{
//Visibility _visible = (new ViewModelLocator()).CallViewModel == null ? Visibility.Collapsed : Visibility.Visible;
return (App.Current.Resources["Locator"] as ViewModelLocator).CallViewModel;
}
set
{
if (this._CurrentCallViewModel != value)
{
this._CurrentCallViewModel = value;
(App.Current.Resources["Locator"] as ViewModelLocator).CallViewModel = value;
NotifyPropertyChanged(m => m.CurrentCallViewModel);
}
}
}
private CallViewModel _CurrentCallViewModel = null;
Anyyou know why this new ViewModel would not bind to the new Model? Is there some mechanism similar to NotifyPropertyChanged that I have to call from the locator to get the objects to bind?
Related
I'm making a UserControl to generate a list of attached files from Dependency Property as ItemSource. But the ItemSource (DependencyProperty) count is 0.
I tried debugging and realized that the ObservableCollection in ViewModel was bound after the Constructor of my UserControl is initialized.
I'm coding in MVVM pattern, I made a function to prepare some sample data for ObservableCollection in ViewModel and inside the MainWindow I bound the DataContext of my UserControl with that ViewModel then set the ItemSource for ObservableCollection
My ViewModel code-behind:
//The properties
ObservableCollection<FileAttachmentModel> filesAttachment;
public ObservableCollection<FileAttachmentModel> FilesAttachment
{
get { return filesAttachment; }
set { filesAttachment = value; OnPropertyChanged("FilesAttachment"); }
}
//The function prepare sample data
private ObservableCollection<FileAttachmentModel> PrepareData()
{
FilesAttachment.Add(new FileAttachmentModel() { FileName = "TrackA", FilePath = "D:\trackA.png" });
FilesAttachment.Add(new FileAttachmentModel() { FileName = "TrackB", FilePath = "D:\trackB.png" });
FilesAttachment.Add(new FileAttachmentModel() { FileName = "TrackC", FilePath = "D:\trackC.png" });
}
My UserControl xaml:
<UserControl x:Class="MailSender.Controls.FileAttachment.FileAttachment"
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"
xmlns:local="clr-namespace:MailSender.Controls.FileAttachment"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800"
DataContext="{Binding RelativeSource={RelativeSource Self}}"
Name="fileAttachmentUC"
>
<Grid>
<WrapPanel DataContext="{Binding ElementName=fileAttachmentUC,Path=DataContext,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}" x:Name="wrapPanel">
</WrapPanel>
</Grid>
</UserControl>
My UserControl code-behind:
//the property ItemSource
public static readonly DependencyProperty ItemSourceProperty = DependencyProperty.Register("ItemSource", typeof(ObservableCollection<FileAttachmentModel>), typeof(FileAttachment),new UIPropertyMetadata());
//the wrapper property
public ObservableCollection<FileAttachmentModel> ItemSource
{
get { return (ObservableCollection<FileAttachmentModel>)GetValue(ItemSourceProperty); }
set { SetValue(ItemSourceProperty, value);
}
}
//the function to generate each file attachment and add them to the Wrappanel in UserControl
//I call this function inside constructor of UserControl and pass ItemSource as parameter
void GenerateFileItem(ObservableCollection<FileAttachmentModel> lstFileAttachment)
{
if (lstFileAttachment != null && lstFileAttachment.Count>0)
{
foreach (var item in lstFileAttachment)
{
StackPanel sp = new StackPanel() { Orientation = Orientation.Horizontal, VerticalAlignment = VerticalAlignment.Center };
TextBlock tbFileName = new TextBlock() { Text = item.FileName };
Button btFilePath = new Button() { Content = "X", Tag = item.FilePath };
btFilePath.Click += BtFilePath_Click;
sp.Children.Add(tbFileName);
sp.Children.Add(btFilePath);
sp.Style = Application.Current.FindResource("stackFileItem") as Style;
wrapPanel.Children.Add(sp);
}
}
}
In Usage:
<control:FileAttachment DataContext="{StaticResource vmMainWindow}" ItemSource="{Binding FilesAttachment,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}"/>
What I expect is to make a container for attached files like Outlook of Microsoft. Please help!
Thanks in advance!
You should call GenerateFileItem whenever the dependency property is set using a PropertyChangedCallback:
public static readonly DependencyProperty ItemSourceProperty = DependencyProperty.Register("ItemSource",
typeof(ObservableCollection<FileAttachmentModel>), typeof(FileAttachment), new PropertyMetadata(new PropertyChangedCallback(OnChanged));
//the wrapper property
public ObservableCollection<FileAttachmentModel> ItemSource
{
get { return (ObservableCollection<FileAttachmentModel>)GetValue(ItemSourceProperty); }
set { SetValue(ItemSourceProperty, value); }
}
private static void OnChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
FileAttachment fa = (FileAttachment)d;
fa.GenerateFileItem(fa.ItemSource);
}
The ItemSource property cannot be set before the UserControl has been initialized.
What I was facing is the ObservableCollection I generate in ViewModel is initialized after the UserControl is initialized. The #mm8 solution has fixed my problem by waiting for the ObservableCollection in ViewModel is initialized first and then pass it by the property I bound in MainWindow. Then the UserControl will be initialized and get the ObservableCollection that is passed from ViewModel then generate the custom controls inside my "wrapPanel".
I have different groups of controls bound to different categories of ViewModel classes.
The ViewModels are
MainViewModel
VideoViewModel
AudioViewModel
Question
How can I set the DataContext with XAML instead of C#?
1. I tried adding DataContext="{Binding VideoViewModel}" to the ComboBox XAML, but it didn't work and the items came up empty.
2. I also tried grouping all the ComboBoxes of a certain category inside a UserControl with the DataContext:
<UserControl DataContext="{Binding VideoViewModel}">
<!-- ComboBoxes in here -->
</UserControl>
3. Also tried setting the <Window> DataContext to itself DataContext="{Binding RelativeSource={RelativeSource Self}}"
Data Context
I'm currently setting the DataContext this way for the different categories of controls:
public MainWindow()
{
InitializeComponent();
// Main
this.DataContext =
tbxInput.DataContext =
tbxOutput.DataContext =
cboPreset.DataContext =
MainViewModel.vm;
// Video
cboVideo_Codec.DataContext =
cboVideo_Quality.DataContext =
tbxVideo_BitRate.DataContext =
cboVideo_Scale.DataContext =
VideoViewModel.vm;
// Audio
cboAudio_Codec.DataContext =
cboAudio_Quality.DataContext =
tbxAudio_BitRate.DataContext =
tbxAudio_Volume.DataContext =
AudioViewModel.vm;
}
XAML ComboBox
<ComboBox x:Name="cboVideo_Quality"
DataContext="{Binding VideoViewModel}"
ItemsSource="{Binding Video_Quality_Items}"
SelectedItem="{Binding Video_Quality_SelectedItem}"
IsEnabled="{Binding Video_Quality_IsEnabled, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
HorizontalAlignment="Left"
VerticalAlignment="Top"
Width="105"
Height="22"
Margin="0,0,0,0"/>
Video ViewModel Class
public class VideoViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged = delegate { };
private void OnPropertyChanged(string prop)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(prop));
}
}
public VideoViewModel() { }
public static VideoViewModel _vm = new VideoViewModel();
public static VideoViewModel vm
{
get { return _vm; }
set
{
_vm = value;
}
}
// Items Source
private List<string> _Video_Quality_Items = new List<string>()
{
"High",
"Medium",
"Low",
};
public List<string> Video_Quality_Items
{
get { return _Video_Quality_Items; }
set
{
_Video_Quality_Items = value;
OnPropertyChanged("Video_Quality_Items");
}
}
// Selected Item
private string _Video_Quality_SelectedItem { get; set; }
public string Video_Quality_SelectedItem
{
get { return _Video_Quality_SelectedItem; }
set
{
if (_Video_Quality_SelectedItem == value)
{
return;
}
_Video_Quality_SelectedItem = value;
OnPropertyChanged("Video_Quality_SelectedItem");
}
}
// Enabled
private bool _Video_Quality_IsEnabled;
public bool Video_Quality_IsEnabled
{
get { return _Video_Quality_IsEnabled; }
set
{
if (_Video_Quality_IsEnabled == value)
{
return;
}
_Video_Quality_IsEnabled = value;
OnPropertyChanged("Video_Quality_IsEnabled");
}
}
}
You can instantiate an object in xaml:
<Window.DataContext>
<local:MainWindowViewmodel/>
</Window.DataContext>
And you could do that for your usercontrol viewmodels as well.
It's more usual for any child viewmodels to be instantiated in the window viewmodel. Exposed as public properties and the datacontext of a child viewmodel then bound to that property.
I suggest you google viewmodel first and take a look at some samples.
I'm not sure if this is the correct way, but I was able to bind groups of ComboBoxes to different ViewModels.
I created one ViewModel to reference them all.
public class VM: INotifyPropertyChanged
{
...
public static MainViewModel MainView { get; set; } = new MainViewModel ();
public static VideoViewModel VideoView { get; set; } = new VideoViewModel ();
public static AudioViewModel AudioView { get; set; } = new AudioViewModel ();
}
I used Andy's suggestion <local:VM> in MainWindow.xaml.
<Window x:Class="MyProgram.MainWindow"
...
xmlns:local="clr-namespace:MyProgram"
>
<Window.DataContext>
<local:VM/>
</Window.DataContext>
And used a UserControl with DataContext set to VideoView, with ComboBoxes inside.
Instead of a UserControl, can also just use VideoView.Your_Property_Name on each binding.
<UserControl DataContext="{Binding VideoView}">
<StackPanel>
<ComboBox x:Name="cboVideo_Quality"
ItemsSource="{Binding Video_Quality_Items}"
SelectedItem="{Binding Video_Quality_SelectedItem}"
IsEnabled="{Binding Video_Quality_IsEnabled, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
HorizontalAlignment="Left"
VerticalAlignment="Top"
Width="105"
Height="22"
Margin="0,0,0,0"/>
<!-- Other ComboBoxes with DataContext VideoView in here -->
</StackPanel>
</UserControl>
Then to access one of the properties:
VM.VideoView.Video_Codec_SelectedItem = "x264";
VM.VideoView.Video_Quality_SelectedItem = "High";
VM.AudioView.Audio_Codec_SelectedItem = "AAC";
VM.AudioView.Audio_Quality_SelectedItem = "320k";
Others have obviously provided good answers, however, the underlying miss of your binding is your first set of DataContext = = = = = to the main view model.
Once you the main form's data context to the MAIN view model, every control there-under is expecting ITS STARTING point as the MAIN view model. Since the MAIN view model does not have a public property UNDER IT of the video and audio view models, it cant find them to bind do.
If you remove the "this.DataContext =", then there would be no default data context and each control SHOULD be able to be bound as you intended them.
So change
this.DataContext =
tbxInput.DataContext =
tbxOutput.DataContext =
cboPreset.DataContext =
MainViewModel.vm;
to
tbxInput.DataContext =
tbxOutput.DataContext =
cboPreset.DataContext =
MainViewModel.vm;
I am struggling for one week now with my problem and i cant solve it. I am programming a MVVM WPF application which is having one window (MainView). In This Mainview i want to load different UserControl when i need them. In the Application-Startup I am loading the MainViewModel. In the Constructor of the MainViewModel I am loading the First ViewModel (LoginViewModel). Cause of my MainView.xaml it is showing me my Login-User-Control like i want to. So till this point everything is fine. In the ActivePanel-class i am saving the CurrentView, because in my MainView.xaml i am making a binding to my CurrentView for the ContentControl. So everything is working except the changing of the views although my NotifyPropertyChanged method of the CurrentView is working. I am thinking, that my mistake is in the xaml (DataBinding). Hope you guys can help me.
This is my MainView.xaml in which i want to load the different DataTemplates. Like I said before: The loading of the LoginViewModel via the Constructor of MainViewModel is working. The changing to other VieModels is working as well, but the DataBinding to the ContentControl is the big problem here.
<Window x:Class="App.View.MainView"
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:App.View"
mc:Ignorable="d"
xmlns:viewmodels="clr-namespace:App.ViewModels"
xmlns:views="clr-namespace:App.View"
xmlns:helper="clr-namespace:App.Helper"
Title="Betrooms" Height="500" Width="350">
<Window.Resources>
<DataTemplate x:Name="LoginUCTemplate" DataType="{x:Type viewmodels:LoginViewModel}">
<views:LoginUC DataContext="{Binding}"/>
</DataTemplate>
<DataTemplate x:Name="RegUCTemplate" DataType="{x:Type viewmodels:RegViewModel}">
<views:RegUC DataContext="{Binding}"/>
</DataTemplate>
<DataTemplate x:Name="HomeUCTemplate" DataType="{x:Type viewmodels:HomeViewModel}">
<views:HomeUC DataContext="{Binding}"/>
</DataTemplate>
</Window.Resources>
<Window.DataContext>
<viewmodels:ActivePanel/>
</Window.DataContext>
<Grid>
<ContentControl Content="{Binding CurrentView, UpdateSourceTrigger=PropertyChanged, Mode=OneWay}"/>
</Grid>
</Window>
This is the class of my ActivePanel where i am saving the information about which ViewModel is the active one. The CurrentView is the property I am binding the Content Control to.
namespace APP.ViewModels
{
public class ActivePanel : NotifyPropertyChanged
{
private object _currentView;
public object CurrentView
{
get { return _currentView; }
set
{
if (value != _currentView)
{
_currentView = value;
OnPropertyChanged("CurrentView");
}
}
}
}
}
This is my MainViewModel:
namespace App.ViewModels
{
public class MainViewModel : ActivePanel
{
private LoginViewModel _loginViewModel;
public MainViewModel()
{
_loginViewModel = new LoginViewModel();
CurrentView = _loginViewModel;
}
}
}
And this is my LoginViewModel where I am changing the value of CurrentView via an action:
namespace App.ViewModels
{
public class LoginViewModel : ActivePanel
{
#region Member
private string _username;
private string _password;
bool login = false;
private HomeViewModel _homeViewModel;
private RegViewModel _regViewModel;
UserModel User = new UserModel();
#endregion
#region Properties
public ICommand RegActionCommand { get; set; }
public ICommand LogActionCommand { get; set; }
public string GetUsername
{
get { return _username; }
set
{
if (value != _username)
{
_username = value;
OnPropertyChanged("GetUsername");
}
}
}
public string GetPassword
{
get { return _password; }
set
{
if (value != _password)
{
_password = value;
OnPropertyChanged("GetPassword");
}
}
}
#endregion
#region Constructor
public LoginViewModel()
{
this.RegActionCommand = new RelayCommand(RegAction);
this.LogActionCommand = new RelayCommand(LogAction);
}
#endregion
#region Button-Action
private void LogAction(object obj)
{
_homeViewModel = new HomeViewModel();
CurrentView = _homeViewModel;
}
private void RegAction(object obj)
{
_regViewModel = new RegViewModel();
CurrentView = _regViewModel;
}
#endregion
}
}
I hope my question is understandable: The ContenControl binding is set to CurrentView but the ContentControl is never changing although the property of CurrentView is changing.
Thanks to you all. Cheers, Paul.
In your command handler, you are changing the CurrentView property of your LoginViewModel. The ContentControl's DataContext is the MainViewModel though, so it's content is bound to the CurrentView property of the MainViewModel.
You need to set the MainViewModel's property in your command handler. There are different ways of achieving this, for example you could add a constructor parameter to the LoginViewModel to pass a reference to the MainViewModel. You can save this reference and then access it in your command handler.
Another possibilty would be to raise an event or send a message from the command in your LoginViewModel and handle it in the MainViewModel. This would reduce the coupling between your ViewModels, but depending on which mechanism and library you use it might be a little bit more complicated.
I am trying to build a rather complex custom control.
I want to use recursion in my View Model to build out a control. I will try and be as clear as possible.
I have two classes, Publisher and Developer
The Publisher class looks like the following:
public class Publisher
{
public Publisher()
{
SubPublishers.CollectionChanged += SubPublishers_CollectionChanged;
ChildDevelopers.CollectionChanged += SubPublishers_CollectionChanged;
}
private ObservableCollection<Publisher> subPublishers;
public ObservableCollection<Publisher> SubPublishers
{
get
{
if (subPublishers == null)
subPublishers = new ObservableCollection<Publisher>();
return subPublishers;
}
}
private ObservableCollection<Developer> childDevelopers;
public ObservableCollection<Developer> ChildDevelopers
{
get
{
if (childDevelopers == null)
childDevelopers = new ObservableCollection<Developer>();
return childDevelopers;
}
}
And my Developer Class looks like this:
public class Developer : NotifyPropertyChanged
{
public Developer(string Title)
{
this.Title = Title;
}
private string title;
public string Title
{
get
{
return this.title;
}
set
{
this.title = value;
OnPropertyChanged("Title");
}
}
So yes, Publisher is n-tier. It can have a Collection of Developers and each of these Developers can have their own Collection of Developers.
Going to my Main View Model:
public class MainViewModel : NotifyPropertyChanged
{
public MainViewModel()
{
this.ParentPublisher = new ParentPublisher();
BuildData();
}
private Publisher parentPublisher;
public Publisher ParentPublisher
{
get
{
return this.parentPublisher;
}
set
{
this.parentPublisher = value;
OnPropertyChanged("ParentPublisher");
}
}
public void BuildData()
{
Publisher firstPublisher = new Publisher();
firstPublisher.ChildDevelopers.Add(new Developer("HAL"));
firstPublisher.ChildDevelopers.Add(new Developer("Retro Games"));
firstPublisher.ChildDevelopers.Add(new Developer("Nintendo"));
Publisher secondPublisher = new Publisher();
secondPublisher.ChildDevelopers.Add(new Developer("343"));
secondPublisher.ChildDevelopers.Add(new Developer("Playground Games"));
secondPublisher.SubPublishers.Add(new Publisher());
secondPublisher.SubPublishers.FirstOrDefault().ChildDevelopers.Add(new Developer("Coalition"));
secondPublisher.SubPublishers.FirstOrDefault().ChildDevelopers.Add(new Developer("Remedy"));
secondPublisher.SubPublishers.FirstOrDefault().SubPublishers.Add(new Publisher());
secondPublisher.SubPublishers.FirstOrDefault().SubPublishers.FirstOrDefault().ChildDevelopers.Add(new Developer("Insomniac"));
secondPublisher.SubPublishers.FirstOrDefault().SubPublishers.FirstOrDefault().ChildDevelopers.Add(new Developer("Criterion"));
secondPublisher.SubPublishers.FirstOrDefault().SubPublishers.FirstOrDefault().ChildDevelopers.Add(new Developer("EA"));
ParentPublisher.Add(firstPublisher);
ParentPublisher.Add(secondPublisher);
}
}
}
So, you can see the possible scenarios here. Now, I was trying to figure out how to build a control off this data.
I want to actually bind to the ParentPublisher because everything added (SubPublishers and the Child Developers) will ultimately be extension of the Parent.
Would I use an ObservableCollection and use the ItemSource to this ParentPublisher?
Any tips or recommendations would be appreciated.
One way is to make a UserControl that excepts the first tier as a DataTemplate. Then you show the data in the DataTemplate as needed. Inside the DataTemplate reference the UserControl again with the inner data.
Example: Obviously modify the layout to benefit you but for simplicity I did it this way.
<UserControl x:Class="WPF_Question_Answer_App.PublisherView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:WPF_Question_Answer_App"
mc:Ignorable="d"
d:DesignHeight="450"
d:DesignWidth="800">
<UserControl.Resources>
<DataTemplate x:Key="DeveloperTemplate">
<TextBlock Text="{Binding Title}" /> <!--show the developer data-->
</DataTemplate>
<DataTemplate x:Key="PublisherTemplate">
<local:PublisherView /> <!-- reference ourself for recursion-->
</DataTemplate>
</UserControl.Resources>
<StackPanel>
<ItemsControl ItemsSource="{Binding ChildDevelopers}"
ItemTemplate="{StaticResource DeveloperTemplate}" />
<ItemsControl ItemsSource="{Binding SubPublishers}"
ItemTemplate="{StaticResource PublisherTemplate}" />
</StackPanel>
</UserControl>
First of all, I have two pages called MainPage and BindPage. The MainPage can navigate to the BindPage. The BindPage have two bindings:
One is DataContext Binding in xaml:
<Page x:Class="MvvmlightTest.View.BindingPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:MvvmlightTest.View"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
xmlns:cvt="using:MvvmlightTest.Converter"
DataContext="{Binding BindingVM, Source={StaticResource Locator}, Mode=OneWay}"
Unloaded="Page_Unloaded">
The other binding is in the page content:
<Grid Background="#7F919191"
Visibility="{x:Bind VM.IsInBuyProcess, Mode=OneWay, Converter={StaticResource BooleanToVisibilityConverter}}">
<ProgressRing Width="100"
Height="100"
HorizontalAlignment="Center"
VerticalAlignment="Center"
IsActive="True" />
</Grid>
The definition of VM is:
public BindingViewModel VM
{
get
{
return DataContext as BindingViewModel;
}
}
The BindingViewModel has just one property:
private bool isInBuyProcess;
public bool IsInBuyProcess
{
get
{
return true;
}
set
{
isInBuyProcess = value;
}
}
And also I need to show BindPage.g.cs, it contain two important classes: BindingPage_obj1_Bindings & BindingPage_obj1_BindingsTracking
BindingPage_obj1_BindingsTracking is the member of BindingPage_obj1_Bindings
public BindingPage_obj1_BindingsTracking bindingsTracking;
public BindingPage_obj1_Bindings()
{
this.bindingsTracking = new BindingPage_obj1_BindingsTracking(this);
Tk = new WeakReference<BindingPage_obj1_BindingsTracking>(this.bindingsTracking);
}
The BindingPage_obj1_Bindings is initialize here:
public global::Windows.UI.Xaml.Markup.IComponentConnector GetBindingConnector(int connectionId, object target)
{
global::Windows.UI.Xaml.Markup.IComponentConnector returnValue = null;
switch(connectionId)
{
case 1:
{
global::Windows.UI.Xaml.Controls.Page element1 = (global::Windows.UI.Xaml.Controls.Page)target;
BindingPage_obj1_Bindings bindings = new BindingPage_obj1_Bindings();
returnValue = bindings;
bindings.SetDataRoot(this);
bindings.SetConverterLookupRoot(this);
this.Bindings = bindings;
element1.Loading += bindings.Loading;
}
break;
}
return returnValue;
}
And BindingPage_obj1_BindingsTracking could not be released outside of BindingPage_obj1_Bindings
private void StopTracking()
{
this.bindingsTracking.ReleaseAllListeners();
this.initialized = false;
}
private void ReleaseAllListeners()
{
UpdateChildListeners_VM(null);
}
private void UpdateChildListeners_VM(global::MvvmlightTest.ViewModel.BindingViewModel obj)
{
if (obj != cache_VM)
{
if (cache_VM != null)
{
((global::System.ComponentModel.INotifyPropertyChanged)cache_VM).PropertyChanged -= PropertyChanged_VM;
cache_VM = null;
}
if (obj != null)
{
cache_VM = obj;
((global::System.ComponentModel.INotifyPropertyChanged)obj).PropertyChanged += PropertyChanged_VM;
}
}
}
After I navigate From MainPage to BindPage, and then go back with GC collection, the BindPage and BindingPage_obj1_Bindings can be released but BindingPage_obj1_BindingsTracking NOT.
If I move the DataContext binding to code-behind and write it to the Page_Loaded event, BindingPage_obj1_BindingsTracking will be released.
So, does the result mean the x:Bind could not be used with Binding?
btw, my test project here
======================Update======================
After some days, I find the result. The solution is quite simple, just call the Bindings.StopTracking() in the Page_Unloaded method, so all the one-way binding and two way binding should be released.
The solution is not a good way to implement more with MVVM because the Bindings property is private and we need to call the function in the code-behind. I don't quite understand why Microsoft generate the Bindings to private.