<Window x:Class="GuessFigure.PlayingGameWindow"
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:GuessFigure"
mc:Ignorable="d"
Title="PlayingGame" Height="300" Width="300">
<Grid
xmlns:c="clr-namespace:GuessFigure.Model">
<Grid.Resources>
<c:Round x:Key="round"/>
</Grid.Resources>
<Grid.DataContext>
<Binding Source=" {StaticResource round}" />
</Grid.DataContext>
<TextBlock x:Name="tbTime" HorizontalAlignment="Left" Margin="108,202,0,0" TextWrapping="Wrap" Text="" VerticalAlignment="Top"/>
<TextBox x:Name="textBox" HorizontalAlignment="Left" Height="23" Margin="123,86,0,0" TextWrapping="Wrap" Text="TextBox" VerticalAlignment="Top" Width="120"/>
<TextBlock x:Name="roundNumber" HorizontalAlignment="Left" Margin="10,10,0,0" TextWrapping="Wrap" VerticalAlignment="Top" Text="{Binding Path=Number}"/>
</Grid>
</Window>
Round.cs:
using GuessFigure.Model.Factory;
using Ninject;
using Ninject.Modules;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Ninject.Planning.Bindings;
namespace GuessFigure.Model
{
/// <summary>
/// 回合
///
/// 每回合限时30s,超时直接进入下一回合
///
/// 题目(数字)由FigureFactory类生产
/// </summary>
///
class Round
{
/// <summary>
/// 回合数
///
/// 一共五回合,每回合出一道猜数字的题目
/// </summary>
internal int Number { get; } = 1;
/// <summary>
/// 生产题目的工厂
/// </summary>
private FigureFactory figureFactory;
[Inject]
internal void SetFigureFactory(FigureFactory figureFactory)
{
this.figureFactory = figureFactory;
}
/// <summary>
/// 调用<see cref=">FigureFactory"/>
/// </summary>
/// <returns>
/// 当前回合的题目。
/// </returns>
public int[] GetCurrentRoundFigures()
{
return figureFactory.Produce(Number);
}
}
class RoundModule : NinjectModule
{
public override void Load()
{
Bind<FigureFactory>().To<FigureFactoryRound1>().When(request=>request.ParentRequest.Target.Type.GetField("number").Equals(1));
Bind<FigureFactory>().To<FigureFactoryRound2>().When(request => request.Target.Type.GetField("number").Equals(2));
Bind<FigureFactory>().To<FigureFactoryRound3>().When(request => request.Target.Type.GetField("number").Equals(3));
Bind<FigureFactory>().To<FigureFactoryRound4>().When(request => request.Target.Type.GetField("number").Equals(4));
Bind<FigureFactory>().To<FigureFactoryRound5>().When(request => request.Target.Type.GetField("number").Equals(5));
}
}
}
I want to bind class Round's property Number to the second TextBlock in Grid and show it to user. However, there was nothing show after I made these code.
What's wrong in my code?
System.Windows.Data Error: 40 : BindingExpression path error: 'Number'
property not found on 'object' ''String' (HashCode=-1455514144)'.
BindingExpression:Path=Number; DataItem='String'
(HashCode=-1455514144); target element is 'TextBlock'
(Name='roundNumber'); target property is 'Text' (type 'String')
Related question may help: WPF/XAML Property not found on 'object'
The property you bind to has to be public, your property is internal. See here.
The properties you use as binding source properties for a binding must be public properties of your class. Explicitly defined interface properties cannot be accessed for binding purposes, nor can protected, private, internal, or virtual properties that have no base implementation.
Also, this:
request.Target.Type.GetField("number").Equals(2)
Will not work since:
Number has a capital N
is a property
GetField("Number") returns aFieldInfo` instance, not the value.
So what you can try is this:
request.Target.Type.GetProperty("number").GetValue(request.Target, null).Equals(2)
First of all, my code has multiple wrongs, and thank Maarten who indicated that
The property you bind to has to be public, your property is internal.
However that was not enough and the problem was still there. I solved my problem at last, by remove the blank space before {StaticResource round} in <Binding Source=" {StaticResource round}" />.
Now the binding runs as I expect.
Your are trying to bind an attribute to a internal property which is wrong. The attribute can only bind to an public property.
Public property is recommend to capitalize first letter.
Related
I'm just getting started with the toolkit and I'm trying to generate a simple ObservableProperty to use with WPF. I create a usercontrol:
<UserControl x:Class="WPF_test.StatusControl"
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:WPF_test"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800">
<Grid>
<TextBox x:Name="txtTest" Text="{Binding testData}" Grid.Column="0" Grid.Row="0" Margin="5,5,5,5" />
</Grid>
</UserControl>
and a ViewModel:
using System;
using CommunityToolkit.Mvvm;
using CommunityToolkit.Mvvm.ComponentModel;
namespace WPF_test
{
[ObservableObject]
public partial class StatusControlViewModel
{
[ObservableProperty]
private String? testData;
}
}
I embed the control into the MainWindow and set the datacontext in codebehind:
public partial class MainWindow : Window
{
StatusControlViewModel model;
public MainWindow()
{
InitializeComponent();
model = new StatusControlViewModel();
status.DataContext = model;
model.testData = "test";
}
}
but I see that model.testData is inaccessible due to its protection level. When I comment this line out in order to get the code to run I get a binding error saying that testData cannot be found.
This is the generated code:
namespace WPF_test
{
partial class StatusControlViewModel
{
/// <inheritdoc cref="testData"/>
[global::System.CodeDom.Compiler.GeneratedCode("CommunityToolkit.Mvvm.SourceGenerators.ObservablePropertyGenerator", "8.0.0.0")]
[global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage]
public string? TestData
{
get => testData;
set
{
if (!global::System.Collections.Generic.EqualityComparer<string?>.Default.Equals(testData, value))
{
OnTestDataChanging(value);
OnPropertyChanging(global::CommunityToolkit.Mvvm.ComponentModel.__Internals.__KnownINotifyPropertyChangingArgs.TestData);
testData = value;
OnTestDataChanged(value);
OnPropertyChanged(global::CommunityToolkit.Mvvm.ComponentModel.__Internals.__KnownINotifyPropertyChangedArgs.TestData);
}
}
}
/// <summary>Executes the logic for when <see cref="TestData"/> is changing.</summary>
[global::System.CodeDom.Compiler.GeneratedCode("CommunityToolkit.Mvvm.SourceGenerators.ObservablePropertyGenerator", "8.0.0.0")]
partial void OnTestDataChanging(string? value);
/// <summary>Executes the logic for when <see cref="TestData"/> just changed.</summary>
[global::System.CodeDom.Compiler.GeneratedCode("CommunityToolkit.Mvvm.SourceGenerators.ObservablePropertyGenerator", "8.0.0.0")]
partial void OnTestDataChanged(string? value);
}
}
It seems that the toolkit is capitalising my property name. I can make the databinding work by capitalising the property name in the control XAML:
<TextBox x:Name="txtTest" Text="{Binding TestData}" Grid.Column="0" Grid.Row="0" Margin="5,5,5,5" />
and similarly access the model property:
model.TestData = "test";
Is there a way to use the toolkit so that the property is accessed in the original form, i.e.
<TextBox x:Name="txtTest" Text="{Binding testData}" Grid.Column="0" Grid.Row="0" Margin="5,5,5,5" />
not
<TextBox x:Name="txtTest" Text="{Binding TestData}" Grid.Column="0" Grid.Row="0" Margin="5,5,5,5" />
? I think it's going to be confusing otherwise.
No.
There is no way "to use the toolkit so that the property is accessed in the original form"
Because that's a field and not a property.
The code generator is literally generating some code.
The property name cannot be the same as your backing field. testData is a field. You cannot bind to a field.
[ObservableProperty]
private String? testData;
Generates a TestData property in the partial class.
public string? TestData
{
get => testData;
set
{
if (!global::System.Collections.Generic.EqualityComparer<string?>.Default.Equals(testData, value))
{
OnTestDataChanging(value);
OnPropertyChanging(global::CommunityToolkit.Mvvm.ComponentModel.__Internals.__KnownINotifyPropertyChangingArgs.TestData);
testData = value;
OnTestDataChanged(value);
OnPropertyChanged(global::CommunityToolkit.Mvvm.ComponentModel.__Internals.__KnownINotifyPropertyChangedArgs.TestData);
}
}
}
You can only bind to public properties so you need what that generates in order to bind. No property means no binding.
Binding:
https://learn.microsoft.com/en-us/dotnet/desktop/wpf/data/?view=netdesktop-6.0
Property:
https://learn.microsoft.com/en-us/dotnet/csharp/programming-guide/classes-and-structs/properties
In the above _seconds is a field.
Hours is a property.
The difference is properties have getters and (optional) setters.
Having said all that.
You are not forced to use that attribute.
You can create your properties manually.
Then you can have whatever case you like for your property.
I suggest you learn to like upper case properties though.
I've worked for a lot of clients. The standard for property names has always been to start with an upper case letter.
PS
[Relaycommand] generates an upper class property with "Command" appended.
i have build my own usercontrol template, the inherited class looks like:
using System.Windows.Controls;
using CustomCopyNas.Views;
namespace CustomCopyNas.MVVM
{
/// <summary>
/// Base class for all Views that is used in MVVM
/// </summary>
/// <typeparam name="TViewModel">ViewModel</typeparam>
public class ViewBase<TViewModel> : UserControl, IView<TViewModel> where TViewModel : UploadViewModelBase
{
public ViewBase()
{ }
public ViewBase(TViewModel tViewModel)
{
ViewModel = tViewModel;
}
/// <summary>
/// ViewModel
/// </summary>
public TViewModel ViewModel
{
get
{
return (TViewModel)DataContext;
}
private set
{
DataContext = value;
}
}
}
}
my xaml file
<mvvm:ViewBase x:Class="CustomCopyNas.Controls.FolderControl"
x:TypeArguments="vm:FolderViewModel"
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:mvvm="clr-namespace:CustomCopyNas.MVVM"
xmlns:vm="clr-namespace:CustomCopyNas.Views"
xmlns:enum="clr-namespace:CustomCopyNas.Enum"
xmlns:sys="clr-namespace:System;assembly=mscorlib"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300" Width="700">
<mvvm:ViewBase.Resources>
<ObjectDataProvider x:Key="osEnum" MethodName="GetValues" ObjectType="{x:Type sys:Enum}">
<ObjectDataProvider.MethodParameters>
<x:Type Type="enum:OsType"></x:Type>
</ObjectDataProvider.MethodParameters>
</ObjectDataProvider>
</mvvm:ViewBase.Resources>
<Grid>
<DataGrid ItemsSource="{Binding Folders, Mode=TwoWay}" AutoGenerateColumns="False">
<DataGrid.Columns>
<DataGridTextColumn Header="Folder or File" Binding="{Binding Path}"/>
</DataGrid.Columns>
</DataGrid>
</Grid>
</mvvm:ViewBase>
and partial class
using CustomCopyNas.MVVM;
using CustomCopyNas.Views;
namespace CustomCopyNas.Controls
{
/// <summary>
/// Interaction logic for FolderControl.xaml
/// </summary>
public partial class FolderControl : ViewBase<FolderViewModel>
{
public FolderControl()
: base(new FolderViewModel("SourceFolders.xml"))
{
InitializeComponent();
}
}
}
When i am trying to compile, i've got error, that the property resources does not exist on viewbase. I counld not figure out, where the error is and my viewbase class is inherited from usercontrol class, this provide resources property.
What is here wrong?
I believe that your problems are caused because generic classes are not supported in XAML. You can read the full story in the Generics in XAML page on MSDN, but in short, from the linked page:
In XAML, a generic type must always be represented as a constrained generic; an unconstrained generic is never present in the XAML type system or a XAML node stream and cannot be represented in XAML markup.
As such, you have a mismatch between your code class declaration:
public class ViewBase<TViewModel>
And your XAML class declaration:
<mvvm:ViewBase x:Class="CustomCopyNas.Controls.FolderControl"
The fact that they do not match will cause you a variety of problems.
Please see the Can I specify a generic type in XAML? and WPF UserControl with generic code-behind questions for further information on this subject.
I have a traditional form layout with a menu bar at the top and status bar at the bottom. When the user selects a menu item, the space in-between (the form's entire remaining client area) gets replaced with a user control - think of an SDI app that can host multiple types of documents.
If you know of a better way to go about this, please chime in. For now, I'm trying to get it to work in a very simplified version with a ContentControl, but I cannot get it to update the screen when its DataContext is set.
Here's the very simple code for ViewModelA. ViewModelB is identical, except for the Bs.
namespace Dynamic_ContentControl
{
public class ViewModelA: ViewModelBase
{
public ViewModelA()
{
DisplayName = "This is A";
}
}
}
The main window is very simple. It basically declares a property to hold the view model of the hosted control and exposes two commands to assign view models A or B.
namespace Dynamic_ContentControl
{
public class MainViewModel: ViewModelBase
{
private ViewModelBase clientContent = null;
public ICommand ShowA { get; private set; }
public ICommand ShowB { get; private set; }
public ViewModelBase ClientContent {
get
{
return clientContent;
}
private set
{
clientContent = value;
OnPropertyChanged("ClientContent");
}
}
public MainViewModel()
{
ShowA = new RelayCommand((obj) =>
{
ClientContent = new ViewModelA();
});
ShowB = new RelayCommand((obj) =>
{
ClientContent = new ViewModelB();
});
}
}
}
Finally, the XAML declares a ContentControl and sets its ContentTemplate to a DataTemplate called ClientAreaTemplate, whose ContentPresenter points to another DataTemplate, named TextBlockLayout:
<Window x:Class="Dynamic_ContentControl.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:vm="clr-namespace:Dynamic_ContentControl"
Title="MainWindow" Height="350" Width="525">
<Window.DataContext>
<vm:MainViewModel />
</Window.DataContext>
<Window.Resources>
<DataTemplate x:Key="TextBlockLayout">
<TextBlock Text="{Binding Path=DisplayName}" />
</DataTemplate>
<DataTemplate x:Key="ButtonLayout">
<Button Content="{Binding Path=DisplayName}" />
</DataTemplate>
<DataTemplate x:Key="CheckBoxLayout">
<CheckBox Content="{Binding Path=DisplayName}" />
</DataTemplate>
<DataTemplate x:Key="ClientAreaTemplate">
<ContentPresenter x:Name="ContentArea" ContentTemplate="{StaticResource ResourceKey=TextBlockLayout}"/>
<DataTemplate.Triggers>
<DataTrigger Binding="{Binding Path=DataContext}"
Value="{x:Type vm:ViewModelB}">
<Setter TargetName="ContentArea"
Property="ContentTemplate"
Value="{StaticResource ResourceKey=ButtonLayout}" />
</DataTrigger>
<DataTrigger Binding="{Binding Path=DataContext}"
Value="{x:Type vm:ViewModelB}">
<Setter TargetName="ContentArea"
Property="ContentTemplate"
Value="{StaticResource ResourceKey=CheckBoxLayout}" />
</DataTrigger>
</DataTemplate.Triggers>
</DataTemplate>
</Window.Resources>
<Grid>
<Button Content="Show A"
Command="{Binding Path=ShowA}"
HorizontalAlignment="Left"
Margin="10,10,0,0"
VerticalAlignment="Top"
Width="75" />
<Button Content="Show B"
Command="{Binding ShowB}"
HorizontalAlignment="Left"
Margin="90,10,0,0"
VerticalAlignment="Top"
Width="75" />
<Label Content="{Binding Path=ClientContent.DisplayName}"
HorizontalAlignment="Left"
Margin="170,8,0,0"
VerticalAlignment="Top" />
<ContentControl DataContext="{Binding Path=ClientContent}"
Content="{Binding}"
ContentTemplate="{StaticResource ResourceKey=ClientAreaTemplate}"
HorizontalAlignment="Left"
Margin="10,37,0,0"
VerticalAlignment="Top"
Height="198"
Width="211" />
</Grid>
</Window>
Expected behaviour
When the screen opens, I want TextBoxLayout to display. If the user then clicks on one of the two buttons, it should load either a ButtonLayout or CheckBoxLayout, depending on the actual runtime type of the view model that is assigned.
Actual behaviour
The screen opens with the TextBoxLayout loaded, but it never changes to another type as I click the buttons.
I think the problem is the way the DataTrigger tries to compare with the type, but there are no binding messages at all the Output window.
In this situation, you need to use DataTemplateSelector:
Provides a way to choose a DataTemplate based on the data object and the data-bound element.
Here is a version of dynamic DataTemplateSelector which returns a desired DataTemplate depending on the type:
/// <summary>
/// Provides a means to specify DataTemplates to be selected from within WPF code
/// </summary>
public class DynamicTemplateSelector : DataTemplateSelector
{
/// <summary>
/// Generic attached property specifying <see cref="Template"/>s
/// used by the <see cref="DynamicTemplateSelector"/>
/// </summary>
/// <remarks>
/// This attached property will allow you to set the templates you wish to be available whenever
/// a control's TemplateSelector is set to an instance of <see cref="DynamicTemplateSelector"/>
/// </remarks>
public static readonly DependencyProperty TemplatesProperty =
DependencyProperty.RegisterAttached("Templates", typeof(TemplateCollection), typeof(DataTemplateSelector),
new FrameworkPropertyMetadata(new TemplateCollection(), FrameworkPropertyMetadataOptions.Inherits));
/// <summary>
/// Gets the value of the <paramref name="element"/>'s attached <see cref="TemplatesProperty"/>
/// </summary>
/// <param name="element">The <see cref="UIElement"/> who's attached template's property you wish to retrieve</param>
/// <returns>The templates used by the givem <paramref name="element"/>
/// when using the <see cref="DynamicTemplateSelector"/></returns>
public static TemplateCollection GetTemplates(UIElement element)
{
return (TemplateCollection)element.GetValue(TemplatesProperty);
}
/// <summary>
/// Sets the value of the <paramref name="element"/>'s attached <see cref="TemplatesProperty"/>
/// </summary>
/// <param name="element">The element to set the property on</param>
/// <param name="collection">The collection of <see cref="Template"/>s to apply to this element</param>
public static void SetTemplates(UIElement element, TemplateCollection collection)
{
element.SetValue(TemplatesProperty, collection);
}
/// <summary>
/// Overriden base method to allow the selection of the correct DataTemplate
/// </summary>
/// <param name="item">The item for which the template should be retrieved</param>
/// <param name="container">The object containing the current item</param>
/// <returns>The <see cref="DataTemplate"/> to use when rendering the <paramref name="item"/></returns>
public override System.Windows.DataTemplate SelectTemplate(object item, System.Windows.DependencyObject container)
{
//This should ensure that the item we are getting is in fact capable of holding our property
//before we attempt to retrieve it.
if (!(container is UIElement))
return base.SelectTemplate(item, container);
//First, we gather all the templates associated with the current control through our dependency property
TemplateCollection templates = GetTemplates(container as UIElement);
if (templates == null || templates.Count == 0)
base.SelectTemplate(item, container);
//Then we go through them checking if any of them match our criteria
foreach (var template in templates)
//In this case, we are checking whether the type of the item
//is the same as the type supported by our DataTemplate
if (template.Value.IsInstanceOfType(item))
//And if it is, then we return that DataTemplate
return template.DataTemplate;
//If all else fails, then we go back to using the default DataTemplate
return base.SelectTemplate(item, container);
}
}
/// <summary>
/// Holds a collection of <see cref="Template"/> items
/// for application as a control's DataTemplate.
/// </summary>
public class TemplateCollection : List<Template>
{
}
/// <summary>
/// Provides a link between a value and a <see cref="DataTemplate"/>
/// for the <see cref="DynamicTemplateSelector"/>
/// </summary>
/// <remarks>
/// In this case, our value is a <see cref="System.Type"/> which we are attempting to match
/// to a <see cref="DataTemplate"/>
/// </remarks>
public class Template : DependencyObject
{
/// <summary>
/// Provides the value used to match this <see cref="DataTemplate"/> to an item
/// </summary>
public static readonly DependencyProperty ValueProperty = DependencyProperty.Register("Value", typeof(Type), typeof(Template));
/// <summary>
/// Provides the <see cref="DataTemplate"/> used to render items matching the <see cref="Value"/>
/// </summary>
public static readonly DependencyProperty DataTemplateProperty =
DependencyProperty.Register("DataTemplate", typeof(DataTemplate), typeof(Template));
/// <summary>
/// Gets or Sets the value used to match this <see cref="DataTemplate"/> to an item
/// </summary>
public Type Value
{ get { return (Type)GetValue(ValueProperty); } set { SetValue(ValueProperty, value); } }
/// <summary>
/// Gets or Sets the <see cref="DataTemplate"/> used to render items matching the <see cref="Value"/>
/// </summary>
public DataTemplate DataTemplate
{ get { return (DataTemplate)GetValue(DataTemplateProperty); } set { SetValue(DataTemplateProperty, value); } }
}
Example of using
<local:DynamicTemplateSelector x:Key="MyTemplateSelector" />
<DataTemplate x:Key="StringTemplate">
<TextBlock>
<Run Text="String: " />
<Run Text="{Binding}" />
</TextBlock>
</DataTemplate>
<DataTemplate x:Key="Int32Template">
<TextBlock>
<Run Text="Int32: " />
<Run Text="{Binding}" />
</TextBlock>
</DataTemplate>
<Style x:Key="MyListStyle" TargetType="ListView">
<Setter Property="ItemTemplateSelector" Value="{StaticResource MyTemplateSelector}"/>
<Setter Property="local:DynamicTemplateSelector.Templates">
<Setter.Value>
<local:Templates>
<local:Template Value={x:Type String} DataTemplate={StaticResource StringTemplate}/>
<local:Template Value={x:Type Int32} DataTemplate={StaticResource Int32Template}/>
</local:Templates>
</Setter.Value>
</Setter>
</Style>
How do we take screenshot in mvvm windows phone application.
I tried the below code but it doesn't work in mvvm app as it requires uielement .Any suggests how to do this in mvvm app ?
var screenshot = new WriteableBitmap(this, null);
var screenshotname = String.Format("Screenshooter_{0}", DateTime.Now.Ticks);
using (var ms = new MemoryStream())
{
screenshot.SaveJpeg(ms, 480, 800, 0, 85);
ms.Seek(0, SeekOrigin.Begin);
var library = new MediaLibrary();
var pic = library.SavePicture(screenshotname, ms);
}
UtilityLib.ShowMessageDialog(string.Concat("Screenshot saved as ", screenshotname));
Try this:
We should register each service in ViewModelLocator, as following:
using Cimbalino.Phone.Toolkit.Services;
using GalaSoft.MvvmLight;
using GalaSoft.MvvmLight.Ioc;
using Microsoft.Practices.ServiceLocation;
/// <summary>
/// This class contains static references to all the view models in the
/// application and provides an entry point for the bindings.
/// </summary>
public class ViewModelLocator
{
/// <summary>
/// Initializes a new instance of the ViewModelLocator class.
/// </summary>
public ViewModelLocator()
{
ServiceLocator.SetLocatorProvider(() => SimpleIoc.Default);
if (!SimpleIoc.Default.IsRegistered<IScreenshotService>())
{
SimpleIoc.Default.Register<IScreenshotService, ScreenshotService>();
}
SimpleIoc.Default.Register<MainViewModel>();
}
/// <summary>
/// Gets the main view model.
/// </summary>
/// <value>
/// The main view model.
/// </value>
public MainViewModel MainViewModel
{
get
{
return ServiceLocator.Current.GetInstance<MainViewModel>();
}
}
public static void Cleanup()
{
// TODO Clear the ViewModels
var viewModelLocator = (ViewModelLocator)App.Current.Resources["Locator"];
viewModelLocator.MainViewModel.Cleanup();
}
}
Then we should implement the MainViewModel as following:
using System.Windows;
using System.Windows.Input;
using System.Windows.Threading;
using Cimbalino.Phone.Toolkit.Services;
using GalaSoft.MvvmLight.Command;
using GalaSoft.MvvmLight;
/// <summary>
/// This class contains properties that the main View can data bind to.
/// </summary>
public class MainViewModel : ViewModelBase
{
/// <summary>
/// The screenshot service
/// </summary>
private readonly IScreenshotService _screenshotService;
/// <summary>
/// Initializes a new instance of the MainViewModel class.
/// </summary>
public MainViewModel(IScreenshotService screenshotService)
{
_screenshotService = screenshotService;
TakeScreenshotCommand = new RelayCommand(TakeScreenshot);
}
/// <summary>
/// Gets the take screenshot command.
/// </summary>
/// <value>
/// The take screenshot command.
/// </value>
public ICommand TakeScreenshotCommand { get; private set; }
/// <summary>
/// Calls to.
/// </summary>
private void TakeScreenshot()
{
_screenshotService.TakeScreenshot("CimbalinoScreenshot");
}
}
for connect view model with the page we should add the ViewModelLocator instance in App.xaml:
XAML
<vm:ViewModelLocator x:Key="Locator" d:IsDataSource="True" />
and add the binding in main page like:
XAML
DataContext="{Binding MainViewModel,
Source={StaticResource Locator}}"
The MainPage.xaml can be the following:
XAML
<phone:PhoneApplicationPage x:Class="CimbalinoSample.MainPage"
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:phone="clr-namespace:Microsoft.Phone.Controls;assembly=Microsoft.Phone"
xmlns:shell="clr-namespace:Microsoft.Phone.Shell;assembly=Microsoft.Phone"
DataContext="{Binding MainViewModel,
Source={StaticResource Locator}}"
FontFamily="{StaticResource PhoneFontFamilyNormal}"
FontSize="{StaticResource PhoneFontSizeNormal}"
Foreground="{StaticResource PhoneForegroundBrush}"
Orientation="Portrait"
SupportedOrientations="Portrait"
shell:SystemTray.IsVisible="True"
mc:Ignorable="d">
<!-- LayoutRoot is the root grid where all page content is placed -->
<Grid x:Name="LayoutRoot" Background="Transparent">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<!-- TitlePanel contains the name of the application and page title -->
<StackPanel x:Name="TitlePanel"
Grid.Row="0"
Margin="12,17,0,28">
<TextBlock Margin="12,0"
Style="{StaticResource PhoneTextTitle2Style}"
Text="Cimbalino Sample" />
<TextBlock Margin="9,-7,0,0"
Style="{StaticResource PhoneTextTitle2Style}"
Text="ScreenshotService" />
</StackPanel>
<!-- ContentPanel - place additional content here -->
<Grid x:Name="ContentPanel"
Grid.Row="1"
Margin="12,0,12,0">
<TextBlock TextWrapping="Wrap">This samples has the goal to show how to use Cimbalino Windows Phone Toolkit Media Library - ScreenshotService</TextBlock>
<Button Margin="0,219,0,293"
Command="{Binding TakeScreenshotCommand}"
Content="Take Screenshot" />
</Grid>
</Grid>
</phone:PhoneApplicationPage>
Source : http://code.msdn.microsoft.com/wpapps/How-to-use-Cimbalino-749562db
I have a UserControl that includes three TextBlock controls. I want to implement three custom properties in UserControl. Something like:
public partial class MyControl: UserControl
{
...
public String Title
{
get { return this.textBlock1.Text; }
set { this.textBlock1.Text = value; }
}
public String Units
{
get { return this.textBlock2.Text; }
set { this.textBlock2.Text = value; }
}
public String Data
{
get { return this.textBlock3.Text; }
set { this.textBlock3.Text = value; }
}
}
If I want to use binding capabilities with these properties I have to implement them as dependency properties. Am I right? But I do not know how to do it in my case.
That is correct. Binding to dependency properties is quite simple to do. Understanding the mechanics I would suggest looking through MSDN. However to answer your question you provide static dependency properties registered to the user control. Then your getters \ setters reference the property.
Here is a sample line of a dependency property.
/// <summary>
/// Provides a bindable text property to the user control
/// </summary>
public static DependencyProperty TextProperty = DependencyProperty.Register("Text", typeof(string), typeof(UserControl1), new PropertyMetadata("", onTextPropertyChanged));
/// <summary>
/// optional static call back handler when the property changed
/// </summary>
/// <param name="o"></param>
/// <param name="e"></param>
static void onTextPropertyChanged(DependencyObject o, DependencyPropertyChangedEventArgs e)
{
var obj = o as UserControl1;
if (obj == null)
return;
//TODO: Changed...
}
/// <summary>
/// Gets \ sets the text
/// </summary>
public string Text
{
get { return (string)this.GetValue(TextProperty); }
set
{
if (this.Text != value)
this.SetValue(TextProperty, value);
}
}
The above is very simple. We register a dependency property TextProperty to UserControl1, this property is the type of string and has a default value of "" (as noted in the property meta data). I also provided a static callback handler if you wish to perform additional steps once the property has changed.
You will then see the Text property uses the GetValue() and SetValue() methods for getting and setting the value of the Text Property.
UPDATE: Binding to a child element in XAML.
This update is to show how to use the the above TextProperty for binding.
Usercontrol1.Xaml. This is the XAML for UserControl1.
<UserControl x:Class="WpfApplication1.UserControl1"
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"
DataContext="{Binding RelativeSource={RelativeSource Self}}"
d:DesignHeight="300" d:DesignWidth="300">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<Label Grid.Column="0" FontWeight="Bold" Content="Text" VerticalAlignment="Center" />
<TextBox Text="{Binding Text, Mode=TwoWay}" Grid.Column="1" VerticalAlignment="Center" Padding="4" />
</Grid>
</UserControl>
My Main Window View Model (Implement INotifyPropertyChanged)
public class MainWindowModel : INotifyPropertyChanged
{
/// <summary>
/// the text
/// </summary>
string myProperty = "This is the default text";
/// <summary>
/// Gets \ sets the text
/// </summary>
public string MyProperty
{
get { return this.myProperty; }
set
{
if (this.MyProperty != value)
{
this.myProperty = value;
this.OnPropertyChanged("MyProperty");
}
}
}
/// <summary>
/// fires the property changed event
/// </summary>
/// <param name="propertyName"></param>
private void OnPropertyChanged(string propertyName)
{
if (this.PropertyChanged != null)
this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
/// <summary>
/// the property changed event
/// </summary>
public event PropertyChangedEventHandler PropertyChanged;
}
MainWindow.Xaml. Binding the text property to the view model
<Window x:Class="WpfApplication1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:ctrl="clr-namespace:WpfApplication1"
Name="Window1"
Title="MainWindow" Height="350" Width="525">
<Window.DataContext>
<ctrl:MainWindowModel />
</Window.DataContext>
<Grid>
<ctrl:UserControl1 Text="{Binding Path=DataContext.MyProperty, Mode=TwoWay, ElementName=Window1}" />
</Grid>
</Window>
Code for the dependency property :
public string Title
{
get { return (string)this.GetValue(TitleProperty); }
set { this.SetValue(TitleProperty, value); }
}
public static readonly DependencyProperty TitleProperty =
DependencyProperty.Register("Title",
typeof(string),
typeof(MyControl),
new PropertyMetadata(null));
Binding in xaml :
<TextBlock Text="{Binding Title,
RelativeSource={RelativeSource FindAncestor,
AncestorType={x:Type yourXmlns:MyControl}}"/>