I have a UserControl called ActionsTreeView I built using MVVM practices where I have an IPluginsProvider interface that populates the data in my UserControl. I want to be able to provide an object implementating this IContentProvider interface as a parameter to initialize my UserControl's ViewModel.
Here is my approach so far, which isn't working. I am wondering if I'm going down the right path? I declare a DependencyProperty in my user control which is visible to my mainWindow where I want to instantiate this UserControl. This code just attempts to pass the PluginsProvider object to my UserControl which needs it to build its ViewModel.
My PluginProvider DependencyProperty setter in my UserControl never gets hit because my My PropertyChanged handler is always null in MainWindow.xaml.cs I think I have the code right, but not sure I'm going down the right road and what I'm missing to make this connection?
ActionsTreeView.xaml.cs
public partial class ActionsTreeView: UserControl
{
public static readonly DependencyProperty PluginProviderProperty = DependencyProperty.Register("PluginProvider", typeof(Models.IPluginsProvider), typeof(ActionsTreeView), new FrameworkPropertyMetadata(null, OnPluginProviderChanged));
private ViewModels.ActionsTreeViewModel vm;
public ActionsTreeView()
{
//Wire-up our ViewModel with the data provider and bind it to DataContext for our user control
//This is a Mock-up until I figure out a way to get the real provider here
Models.IPluginProvider pluginSource = new Models.MockPluginProvider();
vm = new ViewModels.ActionsTreeViewModel(pluginSource );
this.DataContext = vm;
InitializeComponent();
}
private static void OnPluginProviderChanged(DependencyObject source, DependencyPropertyChangedEventArgs e)
{
((ActionsTreeView)source).PluginProvider = (Models.IPluginsProvider)e.NewValue;
}
public Models.IPluginsProvider PluginProvider
{
get
{
return (Models.IPluginsProvider)GetValue(PluginProviderProperty);
}
set
{
SetValue(PluginProviderProperty, value);
vm.SetPluginSource(PluginProvider);
}
}...
MainWindow.xaml.cs
public partial class MainWindow : Window, INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public MainWindow()
{
InitializeComponent();
this.ActionProvider = new Models.PluginsProvider(Library.Action.AvailableActions);
}
private Models.IPluginsProvider _actionProvider;
public Models.IPluginsProvider ActionProvider
{
get { return _actionProvider; }
set
{
_actionProvider = value;
OnPropertyChanged("ActionProvider");
}
}
protected void OnPropertyChanged(string property)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null) //HANDLER IS ALWAYS NULL
{
handler(this, new PropertyChangedEventArgs(property));
}
}
}
Using my UserControl in MainWindow.xaml
<Grid>
<UserControls:ActionsTreeView PluginProvider="{Binding ActionProvider}" />
</Grid>
I don't think you can pass a parameter in the ctor in xaml.
If you create control in code behind you can pass the parameter in the ctor(Param param)
Not sure if this fits in the MVVM model but I use it a lot in regular code behind
Use a frame in the XAML for a place to put the UserControl
Seems like you are missing the binding source
<Grid>
<UserControls:ActionsTreeView PluginProvider="{Binding ActionProvider, RelativeSource={RelativeSource FindAncestor, AncestorType=Window}}" />
</Grid>
since your property ActionProvider is declared in MainWindow so during binding you are required to refer the same source unless you've set it as data context of the window
alternative to above you can also do the below if there is no other data context used in the MainWindow then you can use the original binding you have PluginProvider="{Binding ActionProvider}"
public MainWindow()
{
InitializeComponent();
this.ActionProvider = new Models.PluginsProvider(Library.Action.AvailableActions);
DataContext = this;
}
I've set the DataContext to this which will effectively resolve the value of ActionProvider in binding from the instance this
Extra
you may also choose to remove INotifyPropertyChanged from MainWindow as it is already DependencyObject and capable of property notification and declare a DependencyProperty for ActionProvider
eg
public Models.IPluginsProvider ActionProvider
{
get { return (Models.IPluginsProvider)GetValue(ActionProviderProperty); }
set { SetValue(ActionProviderProperty, value); }
}
// Using a DependencyProperty as the backing store for ActionProvider. This enables animation, styling, binding, etc...
public static readonly DependencyProperty ActionProviderProperty =
DependencyProperty.Register("ActionProvider", typeof(Models.IPluginsProvider), typeof(MainWindow), new PropertyMetadata(null));
so you don't need to worry for the notification change manually, you might be required to use this if the above solution does not work for you otherwise it is good to have.
Related
I'm new to WPF and I'm trying to start a little project with a maximum of good practice. I'm using MVVM and dependency injection.
I have a concern which seems to be easy to understand but i can't find an answer (at this step, DataContext is not very clear for me).
The UserControlView of type UserControl contains just a button for testing.
This is the app class :
public App()
{
IServiceCollection services = new ServiceCollection();
services.AddSingleton<MainWindow>();
services.AddSingleton<UserControlViewModel>();
services.AddSingleton<UserControlView>();
_serviceProvider = services.BuildServiceProvider();
}
The user control is included in the Main windows like that :
<Grid>
<views:UserControlView/>
</Grid>
Now, in the OnStartup overrided method :
protected override void OnStartup(StartupEventArgs e)
{
MainWindow = _serviceProvider.GetRequiredService<MainWindow>();
MainWindow.DataContext = _serviceProvider.GetRequiredService<PaymentMeansViewModel>();
MainWindow.Show();
}
Like that it works, my button is correctly binded to the command.
But what is strange for me is that I have to set the 'UserControlViewModel' as the DataContext of the Main Window.
Isn'it possible to bind it to the 'UserControlView', something like :
protected override void OnStartup(StartupEventArgs e)
{
MainWindow = _serviceProvider.GetRequiredService<MainWindow>();
UserControlView testUC = _serviceProvider.GetRequiredService<UserControlView>();
testUC.DataContext = _serviceProvider.GetRequiredService<UserControlViewModel>();
MainWindow.Show();
}
Thanks for help.
Finally I did it.
I think (I hope I'm right) that I understood.
First of all, let's begin with the basic.
A view must have a viewmodel to bind the properties. A usercontrol is a kind of view "encapsulated" in a view. Therefore a usercontrol must have its own viewmodel and the view must have its own viewmodel.
The datacontext of the MainWindow is set in the app onstartup method :
MainWindow = new MainWindow()
{
DataContext = new MainWindowViewModel()
};
MainWindow must implement INotifyPropertyChanged. All view models must implement this interface. We can create a base class which will be derived in the view models :
public class ViewModelBase : INotifyPropertyChanged
{
public event PropertyChangedEventHandler? PropertyChanged;
protected void OnPropertyChanged(string? propertyName)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
The DataContext of the usercontrol must be explicit in the xaml of the MainWindow:
<Grid>
<views:UserControlView DataContext="{Binding CurrentViewModel}"/>
</Grid>
"CurrentViewModel" is a DataContext, then it's a ViewModel, and as it is binded, it must be a property of the MainViewModel.
public class MainWindowViewModel : ViewModelBase
{
public ViewModelBase CurrentViewModel { get; }
public MainWindowViewModel()
{
CurrentViewModel=new UserControlViewModel();
}
}
Hope it can help.
WPF Data binding doesnt work for custom controls that are defined inside a xaml collection tag. I just want to define a collection of custom widgets inside a custom control and bind some widgets properties against viewmodel properties. Like so.
<Window x:Class="WpfApp1.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:WpfApp1"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<Window.DataContext>
<local:MainViewModel/>
</Window.DataContext>
<Grid>
<local:MyCustomControl>
<local:MyCustomControl.Widgets>
<local:MyCustomWidget ImportantToggle="{Binding SomeToggle}"/>
</local:MyCustomControl.Widgets>
</local:MyCustomControl>
</Grid>
</Window>
That is my custom control. I use an obseravblecollection for the widgets and call SetValue in the constructor to get propertychanged callback later (right now not used in example)
using System.Collections.ObjectModel;
using System.Diagnostics;
using System.Windows;
namespace WpfApp1
{
public class MyCustomControl : FrameworkElement
{
public ObservableCollection<MyCustomWidget> Widgets
{
get { return (ObservableCollection<MyCustomWidget>)this.GetValue(WidgetsProperty); }
set { this.SetValue(WidgetsProperty, value); }
}
public static DependencyProperty WidgetsProperty = DependencyProperty.Register("Widgets", typeof(ObservableCollection<MyCustomWidget>), typeof(MyCustomControl), new PropertyMetadata(null, (e, args) => ((MyCustomControl)e).WidgetsChanged(args)));
public void WidgetsChanged(DependencyPropertyChangedEventArgs e)
{
Debug.WriteLine("widgets collection object changed inside my custom control!");
}
public MyCustomControl()
{
this.SetValue(WidgetsProperty, new ObservableCollection<MyCustomWidget>());
}
}
}
and that is my custom widget:
namespace WpfApp1
{
public class MyCustomWidget : FrameworkContentElement
{
public bool ImportantToggle
{
get { return (bool)this.GetValue(ImportantToggleProperty); }
set { this.SetValue(ImportantToggleProperty, value); }
}
public static DependencyProperty ImportantToggleProperty = DependencyProperty.Register("ImportantToggle", typeof(bool), typeof(MyCustomWidget), new PropertyMetadata(false, (e, args) => ((MyCustomWidget)e).ImportantToggleChanged(args)));
public void ImportantToggleChanged(DependencyPropertyChangedEventArgs e)
{
Debug.WriteLine("my toggle changed inside my custom widget!");
}
}
}
And finally my simplistic ViewModel:
using System;
using System.ComponentModel;
using System.Runtime.CompilerServices;
namespace WpfApp1
{
public class MainViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged([CallerMemberName] String propertyName = "")
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
private bool _someToggle;
public bool SomeToggle
{
get { return this._someToggle; }
set
{
this._someToggle = value;
this.NotifyPropertyChanged();
}
}
public MainViewModel()
{
this.SomeToggle = !this.SomeToggle;
}
}
}
Thats the output I get from Debug.Writeline: widgets collection object changed inside my custom control!
Observation: I cant bind against properties of MyCustomWidget. I understand that the binding might fail in this scenario because the observablecollection is created inside of the constructor of mycustomcontrol, but I dont know how to fix it to get the binding working inside mycustomwidget.
For that binding to work, your local:MyCustomWidget needs to have the same DataContext as the main window. WPF elements inherit their logical parent's DataContext. MyCustomWidget doesn't, because it's not in the logical tree. It's just sitting there. You're not adding it to any kind of normal child collection of its parent, just to a random ObservableCollection that the framework doesn't know about.
The code below is probably a crude hack. I haven't investigated this corner of WPF. I urge you with the utmost sincerity to find out the right way of doing this. But with this addition to your code, I hit the propertychanged event in MyCustomWidget when the binding is initialized.
public MyCustomControl()
{
this.SetValue(WidgetsProperty, new ObservableCollection<MyCustomWidget>());
Widgets.CollectionChanged += Widgets_CollectionChanged;
}
private void Widgets_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
if (e.NewItems is System.Collections.IEnumerable)
{
foreach (MyCustomWidget widget in e.NewItems)
{
AddLogicalChild(widget);
}
}
}
By the way, you can save the trouble of toggling the toggle in the MainViewModel constructor. That happens long before the binding exists. I added a checkbox instead:
<StackPanel>
<CheckBox IsChecked="{Binding SomeToggle}">Test Toggle</CheckBox>
<local:MyCustomControl>
<local:MyCustomControl.Widgets>
<local:MyCustomWidget
ImportantToggle="{Binding SomeToggle}"
/>
</local:MyCustomControl.Widgets>
</local:MyCustomControl>
</StackPanel>
Update:
This omits your Widgets collection entirely, and the binding works without any effort on our part. The child widgets will be in MyCustomControl.Children. Importantly that we aren't limiting the child type to MyCustomWidget any more. That's a significant design change, and may not fit your requirements. You could examine the Panel class closely, and write a class that works the same way, but accepts only one type of child (that would mean writing an analog of UIElementCollection, which will be mostly a big pile of tedious boilerplate).
MyCustomControl.cs
[ContentProperty("Children")]
public class MyCustomControl : Panel
{
}
MyCustomWidget.cs
public class MyCustomWidget : Control
{
public bool ImportantToggle
{
get { return (bool)this.GetValue(ImportantToggleProperty); }
set { this.SetValue(ImportantToggleProperty, value); }
}
public static DependencyProperty ImportantToggleProperty =
DependencyProperty.Register("ImportantToggle", typeof(bool), typeof(MyCustomWidget),
new PropertyMetadata(false, (e, args) => ((MyCustomWidget)e).ImportantToggleChanged(args)));
public void ImportantToggleChanged(DependencyPropertyChangedEventArgs e)
{
Debug.WriteLine("my toggle changed inside my custom widget!");
}
}
MainWindow.xaml
<local:MyCustomControl>
<local:MyCustomWidget
ImportantToggle="{Binding SomeToggle}"
/>
</local:MyCustomControl>
I'm building a Windows Phone 7 app, and I need a reference to my ViewModel in my view so I can set a property from my event handler. The only problem is that I'm not able to get that reference.
What I did;
I have a ViewModelLocator (deleted the irrelevant bits):
static ViewModelLocator()
{
ServiceLocator.SetLocatorProvider(() => SimpleIoc.Default);
SimpleIoc.Default.Register<TunerViewModel>();
}
[SuppressMessage("Microsoft.Performance", "CA1822:MarkMembersAsStatic", Justification = "This non-static member is needed for data binding purposes.")]
public TunerViewModel Tuner
{
get { return ServiceLocator.Current.GetInstance<TunerViewModel>(); }
}
And a view (XAML):
DataContext="{Binding Tuner, Source={StaticResource Locator}}">
And the code-behind of the view:
public partial class Tuner : PhoneApplicationPage
{
private readonly TunerViewModel _viewModel;
public Tuner()
{
_viewModel = DataContext as TunerViewModel;
InitializeComponent();
}
I found this link MVVM View reference to ViewModel where the DataContext is casted to a ViewModel, so I tried the same because it looks like a good solution. However, my _viewModel field is null after the cast. Why is this and how do I fix this? I couldn't find it on Google/Stackoverflow
Thanks in advance :)
Because you set the DataContext from XAML with a binding expression in the View's constructor the DataContext is not set yet. That's why you get null.
Try the cast the DataContext in or after the Loaded event:
public Tuner()
{
InitializeComponent();
Loaded += OnTunerLoaded;
}
private void OnTunerLoaded(object sender, RoutedEventArgs routedEventArgs)
{
_viewModel = DataContext as TunerViewModel;
}
I am trying to create a composite DataContext for a UserControl. Basically I have a control which has Order and Package properties and I wanted to create the composite object representing this datasource in XAML rather than in code.
This is how I am trying to display the UserControl (and create the DataContext):
<views:PackageDetailsControl>
<views:PackageDetailsControl.DataContext>
<vm:OrderPackagePair Package="{Binding Package, Mode=OneWay}"
Order="{Binding Order, Mode=OneWay}"/>
</views:PackageDetailsControl.DataContext>
</views:PackageDetailsControl>
The OrderPackagePair object is a simple dependency object that is created in XAML :
public class OrderPackagePair : DependencyObject
{
public OrderDetails Order
{
get { return (OrderDetails)GetValue(OrderProperty); }
set { SetValue(OrderProperty, value); }
}
public static readonly DependencyProperty OrderProperty =
DependencyProperty.Register("Order", typeof(OrderDetails), typeof(OrderPackagePair), new UIPropertyMetadata(null));
public PackageInfo Package
{
get { return (PackageInfo)GetValue(PackageProperty); }
set { SetValue(PackageProperty, value); }
}
public static readonly DependencyProperty PackageProperty =
DependencyProperty.Register("Package", typeof(PackageInfo), typeof(OrderPackagePair), new UIPropertyMetadata(null));
}
Order and Package are not bound correctly and are just null.
Yes I know there's probably a better way of doing this - but I cannot understand why this isn't working. Occasionally in Blend it'll work and then go blank again.
This will not work because DependencyObject(OrderPackagePair class) doesn't monitor internal changes of its dependency properties. As OrderPackagePair object remains the same, DataContext considered as unchanged.
On the opposite site, class Freezable is intented to notify subscribers that instance was changed when one of its dependency properties changed.
So, try to declare Freezable instead of DependencyObject as base class of OrderPackagePair.
------------- UPDATE --------
Yes, it works. In order to prove it I've implemented simple example.
Code of OrderPackagePairClass:
public class OrderPackagePair : Freezable
{
public OrderDetails Order
{
get { return (OrderDetails)GetValue(OrderProperty); }
set { SetValue(OrderProperty, value); }
}
public static readonly DependencyProperty OrderProperty =
DependencyProperty.Register("Order", typeof(OrderDetails), typeof(OrderPackagePair), new UIPropertyMetadata(null));
public PackageInfo Package
{
get { return (PackageInfo)GetValue(PackageProperty); }
set { SetValue(PackageProperty, value); }
}
public static readonly DependencyProperty PackageProperty =
DependencyProperty.Register("Package", typeof(PackageInfo), typeof(OrderPackagePair), new UIPropertyMetadata(null));
protected override Freezable CreateInstanceCore()
{
throw new NotImplementedException();
}
}
XAML:
<Window x:Class="WindowTest.MainWindow"
xmlns:self="clr-namespace:WindowTest"
Name="RootControl">
<StackPanel Margin="10" DataContextChanged="StackPanel_DataContextChanged">
<StackPanel.DataContext>
<self:OrderPackagePair Package="{Binding Path=DataContext.PackageInfo, Mode=OneWay, ElementName=RootControl}"
Order="{Binding Path=DataContext.OrderDetails, Mode=OneWay, ElementName=RootControl}"/>
</StackPanel.DataContext>
<Button Margin="10" Content="Change Package" Click="Button_Click"/>
</StackPanel>
</Window>
And code behind:
public partial class MainWindow : Window, INotifyPropertyChanged
{
public MainWindow()
{
InitializeComponent();
this.DataContext = this;
}
private OrderDetails _orderDetails;
public OrderDetails OrderDetails
{
get
{
return this._orderDetails;
}
set
{
this._orderDetails = value;
this.OnPropertyChanged("OrderDetails");
}
}
private PackageInfo _packageInfo;
public PackageInfo PackageInfo
{
get
{
return this._packageInfo;
}
set
{
this._packageInfo = value;
this.OnPropertyChanged("PackageInfo");
}
}
private void Button_Click(object sender, RoutedEventArgs e)
{
this.PackageInfo = new PackageInfo(DateTime.Now.ToString());
}
private void StackPanel_DataContextChanged(object sender, DependencyPropertyChangedEventArgs e)
{
Trace.WriteLine("StackPanel.DataContext changed");
}
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string name)
{
var safeEvent = this.PropertyChanged;
if (safeEvent != null)
{
safeEvent(this, new PropertyChangedEventArgs(name));
}
}
}
When you click the button, model changes PackageInfo property (for simplicity model and view are implemented in the same class). Dependency property OrderPackagePair.Package reacts on new value and overwrites its value. Due to Freezable nature, OrderPackagePair notifies all subscribers that it was changed and handler StackPanel_DataContextChanged is called. If you get back to DependencyObject as base class of OrderPackagePair - handler will be never called.
So, I suppose your code doesn't work because of other mistakes. You should carefully work with DataContext. For example, you wrote:
<views:PackageDetailsControl>
<views:PackageDetailsControl.DataContext>
<vm:OrderPackagePair Package="{Binding Package, Mode=OneWay}"
Order="{Binding Order, Mode=OneWay}"/>
</views:PackageDetailsControl.DataContext>
</views:PackageDetailsControl>
and certainly this is one of the problems. Binding expression is oriented on current DataContext. But you set DataContext as OrderPackagePair instance. So you binded OrderPackagePair.Package to OrderPackagePair.Package (I suppose, that your goal is to bind OrderPackagePair.Package to Model.Package). And that's why nothing happened.
In my example in binding expression I explicitly tell to which DataContext I want to bind:
Package="{Binding Path=DataContext.PackageInfo, Mode=OneWay, ElementName=RootControl}"
I’m trying to achieve something that is conceptually quite simple but can’t seem to get it working.
I have a class called c1 it has 2 dependency properties in it an integer I and a string S. It implements INotifiyPropertyChanged.
public class c1: INotifyPropertyChanged
{
private int i;
public int I { get { return i; } set { i = value; if(PropertyChanged != null) PropertyChanged(this,new PropertyChangedEventArgs("I")); } }
private string s;
public string S { get { return s; } set { s = value; if (PropertyChanged != null) PropertyChanged(this, new PropertyChangedEventArgs("S")); } }
#region INotifyPropertyChanged Members
public event PropertyChangedEventHandler PropertyChanged;
#endregion
}
This class is referenced by a Silverlight user control SUC that also implements INotifiyPropertyChanged as a dependency property C, with a PropertyChangedCallback etc. As seen below.
public partial class SUC : UserControl, INotifyPropertyChanged
{
public c1 C
{
get { return (c1)GetValue(CProperty); }
set { SetValue(CProperty, value); }
}
public static readonly DependencyProperty CProperty =
DependencyProperty.Register("C", typeof(c1), typeof(SUC), new PropertyMetadata(new c1(), new PropertyChangedCallback(c1Changed)));
private static void c1Changed(DependencyObject obj, DependencyPropertyChangedEventArgs e)
{
SUC s = obj as SUC;
if (s != null)
s.CChanged((c1)e.NewValue);
}
public void CChanged(c1 c)
{
C = c;
if(PropertyChanged!=null)
PropertyChanged(this,new PropertyChangedEventArgs("C"));
}
public SUC()
{
InitializeComponent();
this.DataContext = this;
}
private void bclick(object sender, RoutedEventArgs e)
{
C.S = C.S + " Clicked";
MessageBox.Show(C.I.ToString() + " - " + C.S);
}
public event PropertyChangedEventHandler PropertyChanged;
}
In my main page which also implements INotifiyPropertyChanged I have an instance of c1 and of SUC.
public partial class MainPage : UserControl, INotifyPropertyChanged
{
public c1 MC
{
get { return (c1)GetValue(MCProperty); }
set { SetValue(MCProperty, value); }
}
public static readonly DependencyProperty MCProperty =
DependencyProperty.Register("MC", typeof(c1), typeof(MainPage), new PropertyMetadata(new c1()));
private static void MCChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
MainPage mp = d as MainPage;
if (mp != null)
mp.MCChanged();
}
public void MCChanged()
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs("MC"));
}
public MainPage()
{
InitializeComponent();
MC.S = "ssss";
this.DataContext = this;
}
public event PropertyChangedEventHandler PropertyChanged;
}
I want to set the C property of the SUC user control via XAML. Like so
local:SUC x:Name="suc" C="{Binding MC, Mode=TwoWay}"
This works well in the c# code behind but not in XAML. The reason I need it in XAML is because I want to bind a collection of c1’s to SUC’s in a DataTemplate.
Any working examples with downloadable code would be most appreciated.
It's a simple little bug in the constructor of the SUC class:
public SUC()
{
InitializeComponent();
this.DataContext = this; //this line shouldn't be here, delete and it will work
}
That means the DataContext of SUC control is itself instead of the MainPage class which is what it needs to be in order to bind to MainPage.MC (the SUC class doesn't have an MC property).
Also, and I realise most of these were you probably just trying to get it to work, but MC does not need to be a DP, you don't need the 'C=c;' line in the SUC, and I wouldn't use the MainPage control class as a datacontext class as well, create another class to bind the DataContext to.
The problem seems to be that you set the DataContext of the UserControl after you load the XAML. Either set it before the XAML is loaded (i.e. before InitializeComponent), or even better, set it in the XAML as such:
<local:MainPage ... DataContext="{Binding RelativeSource={RelativeSource Self}}">
....
</local:MainPage>
The RelativeSource binding specifies that the DataContext of your MainPage should be itself, which seems to be what you want. This then eliminates the assignment of DataContext in code-behind, which is always a good thing in WPF/Silverlight.
Hope that helps.
The DataContext of the UserControl's Controls can be different from the UserControl itself or the UserControl's Parent "Form" (or Parent Page, UserControl). You have to set the Binding in the Code Behind. See this post for more information: Silverlight UserControl Custom Property Binding
Also, You may want to create a Silverlight Control instead of a Silverlight UserControl