Unable to implement WPF MVVM properly - c#

Using ArcGIS Runtime .Net SDK 10.2.7, with MVVM pattern I am getting 'System.NullReferenceException'. in ViewModel constructor at:
this.mapView.Map.Layers.Add(localTiledLayer);
What am I doing wrong?
What I have is:
1- ViewModel.cs
public class ViewModel : INotifyPropertyChanged
{
private Model myModel = null;
private MapView mapView = null;
public event PropertyChangedEventHandler PropertyChanged;
public ViewModel()
{
var URI = "D:/GIS/Runtime/Tutorial/VanSchools.tpk";
ArcGISLocalTiledLayer localTiledLayer = new ArcGISLocalTiledLayer(URI);
localTiledLayer.ID = "SF Basemap";
localTiledLayer.InitializeAsync();
this.mapView.Map.Layers.Add(localTiledLayer);
}
public string TilePackage
{
get { return this.myModel.TilePackage; }
set
{
this.myModel.TilePackage = value;
}
}
2- Model.cs
public class Model
{
private string tilePackage = "";
public Model() { }
public string TilePackage
{
get { return this.tilePackage; }
set
{
if (value != this.tilePackage)
{
this.tilePackage = value;
}
}
}
3- MainWindow.xaml
<Window x:Class="SimpleMVVM.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:esri="http://schemas.esri.com/arcgis/runtime/2013"
xmlns:local="clr-namespace:SimpleMVVM"
mc:Ignorable="d"
Title="MainWindow" Height="350" Width="525">
<Window.Resources>
<local:ViewModel x:Key="VM"/>
</Window.Resources>
<Grid DataContext="{StaticResource VM}">
<Grid.RowDefinitions>
<RowDefinition Height="400" />
<RowDefinition Height="200" />
</Grid.RowDefinitions>
<esri:MapView x:Name="MyMapView" Grid.Row="0"
LayerLoaded="MyMapView_LayerLoaded" >
<esri:Map> </esri:Map>
</esri:MapView>
</Grid>
</Window>
4- MainWindow.xaml.cs
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void MyMapView_LayerLoaded(object sender, LayerLoadedEventArgs e)
{
if (e.LoadError == null)
return;
Debug.WriteLine(string.Format("Error while loading layer : {0} - {1}", e.Layer.ID, e.LoadError.Message));
}
}

It looks like the problem is that mapView has not been set to an instance of an object. Do you need to say something like:
this.mapView = new Mapview();
this.mapView.Map.Layers.Add(localTiledLayer);

It seems that you aren't hooking up the binding but in general you shouldn't have reference to the MapView from your ViewModel and should bind MapView.Map instead. Map is considered being a model that defines how your MapView looks.
In your view make sure that you have the binding setup
<Window.Resources>
<vm:MainViewModel x:Key="vm"></vm:MainViewModel>
</Window.Resources>
<Grid DataContext="{StaticResource vm}">
<esri:MapView x:Name="MyMapView"
Map="{Binding Map}"
LayerLoaded="MyMapView_LayerLoaded">
</esri:MapView>
</Grid>
Then in your ViewModel you can do something like this
public class MainViewModel : INotifyPropertyChanged
{
public MainViewModel()
{
// Create map that is concidered to being a model that defines the content
// of the map
var map = new Map();
// Add your layers to the map
var testServicePath = "https://services.arcgisonline.com/ArcGIS/rest/services/World_Topo_Map/MapServer";
map.Layers.Add(new ArcGISTiledMapServiceLayer(new Uri(testServicePath)));
// map.Layers.Add(new ArcGISLocalTiledLayer("add your path here"));
Map = map;
}
private Map _map;
public Map Map
{
get
{
return _map;
}
set
{
_map = value;
NotifyPropertyChanged(nameof(Map));
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged([CallerMemberName] string propertyName = "")
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}

Related

WPF Nested UserControl Datacontext

I have 2 classes (InnerModel and OuterModel). OuterModel contains 2 InnerModel instances. I would like to create UserControls for them (InnerUserControl and OuterUserControl). OuterUserControl contains 2 InnerUserControls. But I can't figure out how to make binding works in this case.
Below is the complete code of what I try to do.
Please advise how to fix it to get the same result as on a pic at the end.
Thanks in advance!
MainWindow.xaml.cs
<Window x:Class="NestedUserControl.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:NestedUserControl"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="450">
<Grid>
<local:OuterUserControl x:Name="test"/>
</Grid>
MainWindow.xaml
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
var model = new OuterModel("TEST1", "TEST2");
test.DataContext = model;
}
}
InnerModel.cs
public class InnerModel : INotifyPropertyChanged
{
public String Data
{
get { return data; }
set { data = value; }
}
private string data;
public event PropertyChangedEventHandler PropertyChanged;
public InnerModel(string _data) => data = _data;
public void OnPropertyChanged([CallerMemberName]string prop = "") => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(prop));
}
InnerUserControl.xaml
<UserControl x:Class="NestedUserControl.InnerUserControl"
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:NestedUserControl"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="450">
<Grid>
<TextBlock Text="{Binding Path=Data}"/>
</Grid>
InnerUserControl.xaml.cs
public partial class InnerUserControl : UserControl
{
public InnerUserControl()
{
InitializeComponent();
}
}
OuterModel.cs
public class OuterModel : INotifyPropertyChanged
{
public InnerModel model1;
public InnerModel model2;
public event PropertyChangedEventHandler PropertyChanged;
public OuterModel(string data1, string data2)
{
model1 = new InnerModel(data1);
model2 = new InnerModel(data2);
}
public void OnPropertyChanged([CallerMemberName]string prop = "") => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(prop));
}
OuterUserControl.xaml
<UserControl x:Class="NestedUserControl.OuterUserControl"
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:NestedUserControl"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="450">
<Grid>
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition/>
</Grid.RowDefinitions>
<local:InnerUserControl Grid.Row="0" x:Name="inner1" DataContext="model1"/>
<local:InnerUserControl Grid.Row="1" x:Name="inner2" DataContext="model2"/>
</Grid>
OuerUserControl.xaml.cs
public partial class OuterUserControl : UserControl
{
public OuterUserControl()
{
InitializeComponent();
}
}
Working binding MainWindow.xaml.cs
Welcome to SO!
Two problems here, first of all your inner models need to be properties, so change their declarations to this:
public InnerModel model1 {get; set;}
public InnerModel model2 {get; set;}
Second problem is with your bindings, you need to do this instead:
<local:InnerUserControl Grid.Row="0" x:Name="inner1" DataContext="{Binding model1}"/>
<local:InnerUserControl Grid.Row="1" x:Name="inner2" DataContext="{Binding model2}"/>

Asynchronous MVVM for WPF C# using MongoDB

So my situation is this: I want to be able to use MVVM with my WPF application using MongoDB. I am very new to MVVM (I know very little of it), but I've got some experience using .NET and WPF.
I have a namespace for recalling MongoDB collections, with the Model component stored there as a class called "User"
Model (in a separate namespace):
public class User
{
[BsonElement("_id")]
public ObjectId Id { get; set; }
public string name { get; set; }
// other methods listed here
public async static Task<List<User>> getUserList()
{
// allows me to get a list of users
var col = MongoDBServer<User>.openMongoDB("Users");
var filter = Builders<User>.Filter.Exists("name");
List<User> userList = await col.Find(filter).ToListAsync();
return userList;
}
}
I've created a very basic ViewModelBase (abstract ViewModelBase):
public abstract class ViewModelBase : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string propertyName)
{
PropertyChangedEventHandler handler = this.PropertyChanged;
if(handler == null)
{
var e = new PropertyChangedEventArgs(propertyName);
handler(this, e);
}
}
}
And a derived class for handling the User Lists (ViewModel):
public class UserListViewModel : ViewModelBase
{
private User _user;
private ObservableCollection<User> _userList;
public User user
{
get { return _user; }
set
{
_user = value;
OnPropertyChanged("user");
}
}
public ObservableCollection<User> userList
{
get { return _userList; }
set
{
_userList = value;
OnPropertyChanged("userList");
}
}
public UserListViewModel()
{
user = new User();
this.userList = new ObservableCollection<User>();
// since MongoDB operations are asyncrhonous, the async method "getUserList()" is used to fill the observable collection
getUserList().Wait();
}
public async Task getUserList()
{
var UserListRaw = await User.getUserList();
this.userList = new ObservableCollection<User>(UserListRaw);
}
}
The view component is as a simple window with a listbox, as follows (View):
<Window x:Class="UserManagementMVVM.UsersWindow"
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:UserManagementMVVM"
mc:Ignorable="d"
Title="UsersWindow" Height="300" Width="300">
<Window.Resources>
<local:UserListViewModel x:Key="ViewModel"/>
<!-- Receiving error for this XAML block saying "Object reference not set to instance of an object -->
</Window.Resources>
<Grid DataContext="{Binding ViewModel}">
<ListBox Margin="5" ItemsSource="{Binding userList}"/>
</Grid>
</Window>
The App.Xaml and its codebehind are left untouched, as is the View's codebehind.
When I run the program, nothing shows up (ie: The Window starts, but the ListBox is empty even though there is data). I will soon add some button functionality that will perform atomic operations with MongoDB.
I've been trying for nearly 2 weeks to make my own MVVM program for this, with no success. Any assistance would be greatly appreciated.
You are not putting the getUserList() return value into a variable
I assume you mean to do the following
Task.Run(async ()=>this.userList = await getUserList());
this shall work you should think wether you want to wait for the task to finish or not, and than place a .Wait() after it.
Your other issue might be the way you bind to the ViewModel in the context it should use StaticResource instead of binding
like This:
<Grid DataContext="{StaticResource ViewModel}">
<Window x:Class="UserManagementMVVM.UsersWindow"
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:UserManagementMVVM"
mc:Ignorable="d"
Title="UsersWindow" Height="300" Width="300">
<Window.DataContext>
<!--You have to set the DataContext -->
<local:UserListViewModel x:Key="ViewModel"/>
</Window.DataContext>
<Grid>
<ListBox Margin="5" ItemsSource="{Binding userList}"/>
</Grid>
</Window>
You have to set the DataContext right. i changed your xaml. but i prefer setting the DataContext for the Mainwindow in Codebehind or app.xaml.cs.
eg: app.xaml.cs
protected override void OnStartup(StartupEventArgs e)
{
var data = new MainWindowViewmodel();
this.MainWindow = new MainWindow(data);
this.MainWindow.Show();
}
all other DataContext for my views are done with DataTemplates within the ResourceDictionary
<DataTemplate DataType="{x:Type local:MyOtherViewmodel}">
<local::MyOtherViewmodelView />
</DataTemplate>
I want to give credit to both gilMishal and blindmeis for pointing me in the right direction. Both of your answers have helped. Here is my updated (and functional!) code:
App.xaml.cs has been modified as follows (Credit to blindmeis):
public partial class App : Application
{
protected override void OnStartup(StartupEventArgs e)
{
base.OnStartup(e);
UsersWindow window = new UsersWindow();
var ViewModel = new UserListViewModel();
window.DataContext = ViewModel;
window.Show();
}
}
The ViewModel has been updated:
public class UserListViewModel : ViewModelBase
{
private User _user;
private ObservableCollection<string> _userList; // changed from "User" class to string
public User user
{
get { return _user; }
set
{
_user = value;
OnPropertyChanged("user");
}
}
public ObservableCollection<string> userList
{
get { return _userList; }
set
{
_userList = value;
OnPropertyChanged("userList");
}
}
public UserListViewModel()
{
userList = new ObservableCollection<string>();
Task.Run(async () => this.userList = await getUserList()); // Credit to gilMishal
}
public async Task<ObservableCollection<string>> getUserList()
{
var UserListRaw = await User.getUserList();
var userListOC = new ObservableCollection<string>();
foreach (var doc in UserListRaw) // extracting the "name" property from each "User" object
{
userListOC.Add(doc.name);
}
return userListOC;
}
}
And the view:
<Window x:Class="UserManagementMVVM.UsersWindow"
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:UserManagementMVVM"
mc:Ignorable="d"
Title="UsersWindow" Height="300" Width="300">
<Window.Resources>
<local:UserListViewModel x:Key="ViewModel"/>
</Window.Resources>
<Grid> <!-- data context removed from here, credit blindmeis -->
<ListBox Margin="5" ItemsSource="{Binding userList}"/>
</Grid>
</Window>

WPF Data binding Label content

I'm trying to create a simple WPF Application using data binding.
The code seems fine, but my view is not updating when I'm updating my property.
Here's my XAML:
<Window x:Class="Calculator.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:calculator="clr-namespace:Calculator"
Title="MainWindow" Height="350" Width="525"
Name="MainWindowName">
<Grid>
<Label Name="MyLabel" Background="LightGray" FontSize="17pt" HorizontalContentAlignment="Right" Margin="10,10,10,0" VerticalAlignment="Top" Height="40"
Content="{Binding Path=CalculatorOutput, UpdateSourceTrigger=PropertyChanged}"/>
</Grid>
</Window>
Here's my code-behind:
namespace Calculator
{
public partial class MainWindow
{
public MainWindow()
{
DataContext = new CalculatorViewModel();
InitializeComponent();
}
}
}
Here's my view-model
namespace Calculator
{
public class CalculatorViewModel : INotifyPropertyChanged
{
private String _calculatorOutput;
private String CalculatorOutput
{
set
{
_calculatorOutput = value;
NotifyPropertyChanged();
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void NotifyPropertyChanged([CallerMemberName] String propertyName = "")
{
var handler = PropertyChanged;
if (handler != null)
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
}
I'm can't see what I am missing here?? o.O
CalculatorOutput has no getter. How should the View get the value? The Property has to be public as well.
public String CalculatorOutput
{
get { return _calculatorOutput; }
set
{
_calculatorOutput = value;
NotifyPropertyChanged();
}
}

How do you perform Binding with a DataGridView in WPF?

I want to bind a datagrid view in a user control that is docking to a main WPF form. However everytime I try to bind the data it must pre exist and won't update. Is there a way to perform this in the XAML directly to know when an event is triggered to update the datagridview rather than do it in the code behind?
Partial code of XAML:
xmlns:c="clr-namespace:TestWPFMain"
<UserControl.Resources>
<c:GridData x:Key="dataforGrid"/>
</UserControl.Resources>
<Grid>
<DataGrid Grid.Row="2" x:Name="datagridMain" ItemsSource="{Binding Source={StaticResource dataforGrid}, Path=Results, Mode=TwoWay}" />
</Grid>
Code Behind for UserControl above:
public GridControl()
{
InitializeComponent();
GridData gd = new GridData();
gd.UpdateResults();
//datagridMain.ItemsSource = gd.Results;
-- This code above will work if I uncomment but I want it to be bound
directly and was curious as I thought the mode of 'two way' would
do this. I am not certain and most examples assume property is already
set up and not being created and updated.
}
Code Class for GridData:
class PersonName
{
public string Name { get; set; }
}
class GridData
{
public ObservableCollection<PersonName> Results { get; set; }
public void UpdateResults()
{
using (EntityDataModel be = new EntityDataModel())
{
var list = be.tePersons.Select(x => new PersonName { Name = x.FirstName });
Results = new ObservableCollection<PersonName>(list);
}
}
}
To use binding like this, you need to:
Set the DataContext correctly on the DataGrid (or on one of its parent)
Implement INotifyPropertyChanged on your model class, and raise PropertyChanged in the property setter.
1)
Set your window's DataContext to the GridData object:
public GridControl()
{
InitializeComponent();
GridData gd = new GridData();
gd.UpdateResults();
this.DataContext = gd;
}
2)
Implement INotifyPropertyChanged. This ensures that your view gets notified when the Results property gets updated:
public class GridData : INotifyPropertyChanged
{
private ObservableCollection<PersonName> _results;
public ObservableCollection<PersonName> Results
{
get { return _results; }
set
{
_results = value;
RaisePropertyChanged("GridData");
}
}
// ...
#region INotifyPropertyChanged
public event PropertyChangedEventHandler PropertyChanged;
private void RaisePropertyChanged(string prop)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(prop));
}
}
#endregion
}
Then you can simply bind to the path relative to the data context.
<DataGrid ItemsSource="{Binding Results}" />
Note that you don't need two-way binding in this case -- that's for propagating changes from the View back to your model (ie, most useful for when there's a UI control like a text box or checkbox).
Here is an example (I used Window, but it will work the same for UserControl)
Xaml:
<Window x:Class="WpfApplication4.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525" Name="UI">
<Grid>
<DataGrid Grid.Row="2" x:Name="datagridMain" ItemsSource="{Binding ElementName=UI, Path=GridData.Results, Mode=TwoWay}" />
</Grid>
</Window>
or id you want the whole DataContext:
<Window x:Class="WpfApplication4.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525" Name="UI">
<Grid>
<DataGrid Grid.Row="2" x:Name="datagridMain" DataContext="{Binding ElementName=UI, Path=GridData}" ItemsSource="{Binding Results}" />
</Grid>
</Window>
Code:
You will have to implement INotifyPropertyChanged so the xaml knows GridData has changed
The ObservableCollection inside GridData as this function built-in so anytime you add remove items they will update the DataGrid control
public partial class MainWindow : Window , INotifyPropertyChanged
{
public MainWindow()
{
InitializeComponent();
GridData = new GridData { Results = new ObservableCollection<PersonName>() };
GridData.Results.Add(new PersonName { Name = "Test1" });
GridData.Results.Add(new PersonName { Name = "Test2" });
}
private GridData _gridData;
public GridData GridData
{
get { return _gridData; }
set { _gridData = value; NotifyPropertyChanged("GridData"); }
}
public event PropertyChangedEventHandler PropertyChanged;
/// <summary>
/// Notifies the property changed.
/// </summary>
/// <param name="info">The info.</param>
public void NotifyPropertyChanged(String info)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(info));
}
}
}
Classes:
I made a small change to the update method, so it just clears and updates the existing ObservableCollection, otherwise you would have to Implement INotifypropertyChanged to this class if you assign a new ObservableCollection.
public class PersonName
{
public string Name { get; set; }
}
public class GridData
{
public GridData()
{
Results = new ObservableCollection<PersonName>()
}
public ObservableCollection<PersonName> Results { get; set; }
public void UpdateResults()
{
using (EntityDataModel be = new EntityDataModel())
{
// Just update existing list, instead of creating a new one.
Results.Clear();
be.tePersons.Select(x => new PersonName { Name = x.FirstName }).ToList().ForEach(item => Results.Add(item);
}
}
}

PropertyChanged event null after setting DataContext

I am setting the DataContext for my View in the View's Constructor to an instance of my ViewModel, just standard stuff. Shortly thereafter, an UPDATE_RECENT_DOCUMENTS_LIST Event fires from the Event Aggregator which my ViewModel catches correctly. A property is changed and the onPropertyChanged method is called, but it fails as the PropertyChanged event is null.
The very next thing I do is an action to the UI which raises a CREATE_PROJECT Event and the same ViewModel is receiving events, except now, the PropertyChanged event is no longer null and everything works as expected.
Is there a specific amount of time that has to pass after setting the DataContext before it registers to the PropertyChanged Event? Is there an event I can wait for that ensures the PropertyChanged event is not null?
Also, I did not run into this problem using standard .NET events, just after integrating Prism and using the very convenient EventAggregator.
I am showing my code behind of the View and the ViewModel, omitting the View XAML for brevity.
ToolBarView.xaml.cs:
namespace ToolBarModule
{
public partial class ToolBarView : UserControl
{
public ToolBarView(ToolBarViewModel toolBarViewModel)
{
InitializeComponent();
this.DataContext = toolBarViewModel;
}
}
}
ToolBarViewModel.cs
namespace ToolBarModule
{
public class ToolBarViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private ToolBarCommands baseCommands;
private IEventAggregator eventAggregator;
private KickStartEvent kickStartEvent;
private SubscriptionToken subscriptionToken;
private ObservableCollection<IDocumentReference> recentDocuments = new ObservableCollection<IDocumentReference>();
private ActionCommand newTest;
private ActionCommand openTest;
private ActionCommand saveTest;
private ActionCommand exitApplication;
public ToolBarViewModel(){}
public ToolBarViewModel(IEventAggregator eventAggregator)
{
this.eventAggregator = eventAggregator;
baseCommands = new ToolBarCommands(eventAggregator);
kickStartEvent = eventAggregator.GetEvent<KickStartEvent>();
subscriptionToken = kickStartEvent.Subscribe(kickStartEventHandler, ThreadOption.UIThread, true, toolBarEventHandlerFilter);
}
public ICommand NewTest
{
get
{
if (newTest == null)
{
newTest = new ActionCommand(baseCommands.NewTestAction);
}
return newTest;
}
}
public ICommand OpenTest
{
get
{
if (openTest == null)
{
openTest = new ActionCommand(baseCommands.OpenTestAction);
}
return openTest;
}
}
public ICommand SaveTest
{
get
{
if (saveTest == null)
{
saveTest = new ActionCommand(baseCommands.SaveTestAction);
}
return saveTest;
}
}
public ICommand ExitApplication
{
get
{
if (exitApplication == null)
{
exitApplication = new ActionCommand(baseCommands.ExitApplicationAction);
}
return exitApplication;
}
}
public ObservableCollection<IDocumentReference> RecentDocuments
{
get
{
return recentDocuments;
}
set
{
recentDocuments = value;
onPropertyChanged("RecentDocuments");
}
}
private void onPropertyChanged(string propertyChanged)
{
if (PropertyChanged != null)
{
PropertyChanged(this,new PropertyChangedEventArgs(propertyChanged));
}
}
private void kickStartEventHandler(KickStartEventsArgs e)
{
switch (e.EventType)
{
case KickStartEventsArgs.KickStartEventType.CREATE_PROJECT:
onPropertyChanged("RecentDocuments");
break;
case KickStartEventsArgs.KickStartEventType.UPDATE_RECENT_DOCUMENTS_LIST:
RecentDocuments.Clear();
foreach (IDocumentReference recentDocs in e.KickStartTestList)
{
RecentDocuments.Add(recentDocs);
}
onPropertyChanged("RecentDocuments");
break;
}
}
}
}
You can also try to set the DataContext of a Grid or an Element below the UserControl. For me it worked.
Example (Doesn't work if you use DependencyProperty):
Code Behind:
public MyUserControl()
{
InitializeComponent();
this.DataContext = new { LabelText = "Hello World!" };
}
XAML
<UserControl x:Class="CoolProject.ViewModel.MyUserControl"
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"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800">
<Label x:Name="myLabel" Content="{Binding LabelText}"/>
Example 2 (My working code):
Code Behind:
public MyUserControl()
{
InitializeComponent();
this.myGrid.DataContext = new { LabelText = "Hello World!" };
}
XAML
<UserControl x:Class="CoolProject.ViewModel.MyUserControl"
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"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800">
<Grid x:Name="myGrid">
<Label x:Name="myLabel" Content="{Binding LabelText}"/>
</Grid>
You have to name your UserControl in XAML and use it in binding. Something like following code:
<UserControl x:Name="uc" >
.
.
.
<TextBox Text="{Binding UserName, Mode=TwoWay, ElementName=uc}"/>
Where uc is a name of your UserControl, and Also try to set DataContext when UserControl loaded.
Hope this help.

Categories