Can a markup extension be aliased? - c#

The markup extension that brought me to ask this is Catel's LanguageBinding.
I was until now using Infralution's localization assembly, which works more or less the same way.
Catel:
<TextBlock Text="{LanguageBinding MyText}"/>
Infralution:
<TextBlock Text="{Resx MyText}"/>
But as you can see, the markup extension is way shorter to write, thus less prone to typos.
So I wanted to know if there was any way to be able to use LanguageBinding with another markup extension word, like:
Ideal:
<TextBlock Text="{LB MyText}"/>
I'm well aware of readability issues and such, it's an example.

That's not possible to do directly in XAML but you can derive a class from LanguageBinding and use it. Here's an example for shortening the StaticResource Markup Extension.
class SR : StaticResourceExtension
{
public SR() {}
public SR(object resourceKey)
:base(resourceKey)
{ }
}
Now you need can use something like {local:SR} as an "alias".

You can inherit MarkupExtension to create your own custom Binding tag
[MarkupExtensionReturnType(typeof(object))]
public class LBBinding : MarkupExtension
{
private Binding _binding = new Binding();
public Binding Binding
{
get { return _binding; }
set { _binding = value; }
}
public PropertyPath Path
{
get { return _binding.Path; }
set { _binding.Path = value; }
}
<TextBox Text="{customBinding:LBBinding Path=DummyString}"></TextBox>
You should also override ProvideValue method from MarkupExtension. This method will be trigger whenever WPF perform the actual binding. Use the IServiceProvider to get back your DependencyObject (Control) and the DependencyProperty (Your binding property). Then you can do all the magic you want with those 2 information.
public override object ProvideValue(IServiceProvider provider)
{
var service = (IProvideValueTarget)provider.GetService(typeof(IProvideValueTarget));

Related

Binding an event to a method, why does it work in UWP?

UWP came with a new way of DataBinding, Compiled Binding, using the {x:Bind} markup extension, when I was discovering this new feature, I found out that we can actually bind an event to a method !
Example :
Xaml :
<Grid>
<Button Click="{x:Bind Run}" Content="{x:Bind ButtonText}"></Button>
</Grid>
Code Behind :
private string _buttonText;
public string ButtonText
{
get { return _buttonText; }
set
{
_buttonText = value;
OnPropertyChanged();
}
}
public MainPage()
{
this.InitializeComponent();
ButtonText = "Click !";
}
public async void Run()
{
await new MessageDialog("Yeah the binding worked !!").ShowAsync();
}
The result :
And since {x:Bind} bindings are evaluated at runtime and the compiler generates some files that represent that binding, so I went there to investigate what's going on, so in the MainPage.g.cs file (MainPage is the xaml file in question) I found this :
// IComponentConnector
public void Connect(int connectionId, global::System.Object target)
{
switch(connectionId)
{
case 2:
this.obj2 = (global::Windows.UI.Xaml.Controls.Button)target;
((global::Windows.UI.Xaml.Controls.Button)target).Click += (global::System.Object param0, global::Windows.UI.Xaml.RoutedEventArgs param1) =>
{
this.dataRoot.Run();
};
break;
default:
break;
}
}
The compiler seems to know that it's a valid binding, moreover it creates the corresponding event handler, and it calls the concerned method inside.
That is great ! but why ?? A binding target should be a dependency property, not an event. The official documentation for {x:Bind} does mention this new feature, but doesn't explain why and how can a non dependency property be a target of binding, anyone who has a deep explanation for this ?
Well, it's sort of the new feature of the "x:Bind" compiled binding.
https://msdn.microsoft.com/en-us/library/windows/apps/mt204783.aspx
Event binding is a new feature for compiled binding. It enables you to specify the handler for an event using a binding, rather than it having to be a method on the code behind. For example: Click="{x:Bind rootFrame.GoForward}".
For events, the target method must not be overloaded and must also:
Match the signature of the event.
OR have no parameters.
OR have the same number of parameters of types that are assignable from the types of the event parameters.
I guess your scenario perfectly match item 2.
A binding target should be a dependency property
While this is true for a regular binding, it does not hold for the {x:Bind} markup extension.
So you can in fact have a non-dependency property like e.g.
public sealed partial class MyUserControl : UserControl
{
public Color BackgroundColor
{
get { return ((SolidColorBrush)Background).Color; }
set { Background = new SolidColorBrush(value); }
}
}
as an {x:Bind} target like this:
<local:MyUserControl BackgroundColor="{x:Bind ViewModel.BgColor}" />
while
<local:MyUserControl BackgroundColor="{Binding ViewModel.BgColor}" />
would fail.

How to control View VisualState through ViewModel

The question is: How to get/set the VisualState of a Control (with more than two Visual States) on the View through my ViewModel in MVVM pattern (with zero-view-code-behind)?
I've seen similar questions who's answers didn't work for me:
Binding [VisualStateManager] view state to a MVVM viewmodel?
How to change VisualState via ViewModel
Note: below I'll be explaining what was wrong with the answers in the mentioned questions. If you know a better approach, you can dismiss reading the rest of this question.
As for the first question, the accepted answer's approach doesn't work for me. Once I type the mentioned XAML code
<Window .. xmlns:local="clr-namespace:mynamespace" ..>
<TextBox Text="{Binding Path=Name, Mode=TwoWay}"
local:StateHelper.State="{Binding Path=State, Mode=TwoWay}" />
</Window>
It shows a design-time error that says: The attachable property 'State' was not found in type 'StateHelper'., I tried to get over this by renaming StateHelper.StateProperty to StateHelper.State, ending up with two errors..
1: The attachable property 'State' was not found in type 'StateHelper'. and
2: The local property "State" can only be applied to types that are derived from "StateHelper".
As for the second question, the accepted answer's approach doesn't work for me. After fixing VisualStateSettingBehavior's syntax errors to be:
public class VisualStateSettingBehavior : Behavior<Control>
{
private string sts;
public string StateToSet
{
get { return sts; }
set
{
sts = value;
LoadState();
}
}
void LoadState()
{
VisualStateManager.GoToState(AssociatedObject, sts, false);
}
}
I got a design-time error on the line
<local:VisualStateSettingBehavior StateToSet="{Binding State}"/>
that says: A 'Binding' cannot be set on the 'StateToSet' property of type 'VisualStateSettingBehavior'. A 'Binding' can only be set on a DependencyProperty of a DependencyObject.
I tried to merge the two solutions by making VisualStateSettingBehavior.StateToSet a dependency property, but I got other design-time errors in the View.
Any suggestions?
At last, I could solve this. The solution was similar to the first question's best answer. I found out that in my case there are some constraints on the View.xaml to use an attached property:
It has to be registered via DependencyProperty.RegisterAttached.
It has to be static.
It must has a property instance (getter/setter).
I got through that with this coding-style in mind, and the final approach was:
VisualStateApplier:
public class VisualStateApplier
{
public static string GetVisualState(DependencyObject target)
{
return target.GetValue(VisualStateProperty) as string;
}
public static void SetVisualState(DependencyObject target, string value)
{
target.SetValue(VisualStateProperty, value);
}
public static readonly DependencyProperty VisualStateProperty =
DependencyProperty.RegisterAttached("VisualState", typeof(string), typeof(VisualStateApplier), new PropertyMetadata(VisualStatePropertyChangedCallback));
private static void VisualStatePropertyChangedCallback(DependencyObject target, DependencyPropertyChangedEventArgs args)
{
VisualStateManager.GoToElementState((FrameworkElement)target, args.NewValue as string, true); // <- for UIElements, OR:
//VisualStateManager.GoToState((FrameworkElement)target, args.NewValue as string, true); // <- for Controls
}
}
View:
<!--A property inside the object that owns the states.-->
<local:VisualStateApplier.VisualState>
<Binding Path="State"/>
</local:VisualStateApplier.VisualState>
ViewModel:
private string _state;
public string State
{
get { return _state; }
set
{
_state = value;
RaisePropertyChanged("State");
}
}

Quick way to link Objects from screen to View Model

Out of curiosity, is there a way to do this quicker without defining two string or objects?
Xaml
<TextBox Margin="5" Width="100" Text={Binding Path=dataString}></TextBox>
View Model
string _dataString;
public string dataString
{
get
{
return _dataString;
}
set
{
_dataString = value;
base.OnPropertyChanged();
}
}
You can define helpers to shorten the syntax somewhat. For example, if you use the MVVM Light Toolkit, and inherit your ViewModel from ViewModelBase, the toolkit provides a helper that enables use of the following syntax:
private string _dataString = null;
public string DataString
{
get { return _dataString; }
set { Set(ref _dataString, value); }
}
You still have to provide a backing field, but the helper takes care of notifying the exact property that changed, and only raises the event if the new value is in fact different from the current one.
You can also speed up the process of adding the properties by creating a custom code snippet and importing it into Visual Studio via the Code Snippets Manager.

WPF Data Binding TooWay XAML to C# and C# to XAML

I have XAML code:
<TextBox Name="textBoxMask1"/>
<TextBox Name="textBoxMask2"/>
<TextBox Name="textBoxMask3"/>
...
<TextBox Name="textBoxMask9"/>
and class in C#:
private static string mask1;
public static string Mask1
{
get { return mask1; }
set { mask1 = value; }
}
private static string mask2;
public static string Mask2
{
get { return mask2; }
set { mask2 = value; }
}
private static string mask3;
public static string Mask3
{
get { return mask3; }
set { mask3 = value; }
}
....
private static string mask9;
public static string Mask9
{
get { return mask9; }
set { mask9 = value; }
}
And I want to bind these TextBoxes with Properties -> textBoxMask1 with Mask1 etc.
Earlier I did this by TextChanged, but I want to make Binding. TooWay Binding, because I want to predefine Mask1, Mask2, Mask3, ..., Mask9 in another C# class, and maybe later change these values - also in some C# code - and I want my changes, to be visible in layout (XAML) and in C# code - so ex. changing Property Mask1 from C# will change Text in TextBox textBoxMask1, and changing Text in textBoxMask1 will change Property Mask1.
I don't understand, how to make connection (binding) between objects XAML and C#.
For a normal Binding you don't need your properties to be static, just public. Here an example:
C# code (for one property)
private string mask1;
public string Mask1
{
get { return mask1; }
set
{
mask2 = value;
RaisePropertyChanged("Mask1");
}
}
It's really important for the binding that the class containing the properties implements the INotifyPropertyChanged interface, and that you raise the corresponding event in the setter of each property. Another option is to make all properties DependecyProperty, but it is usually overkill.
As for the XAML:
<TextBox Name="textBoxMask1" Text="{Binding Mask1, Mode=TwoWay}"/>
(TwoWay Binding is the default for the Text property, but it does not hurt to put it explicitly).
Just make sure that the DataContext of the object containing your TexBoxes (usually an UserControl) is set to a valid instance of your C# class.
By the way, this is a very basic question, that's why you got a negative vote and no answers before mine. What is expected is that you ask a question that poses a real problem for you, with a very specific answer, not something like "teach me how to do this".
If this answers your question don't forget to mark it as answer (the "tick" mark on the top left). A vote up would be also appreciated.
Hope it helps, regards.

How to use Custom Dependency Properties directly in WPF?

Edit: I changed the code according to Thorstens Answer, using the enum, but did not work.
I am using Dependency Properties to influence a WPF control I am creating. I'm new to WPF, so I'm not sure what I am doing wrong and I can't find proper articles explaining it.
For example, I'm trying to define the Visibility of a control via Dep Properties. The property, in this case, would be this:
public static readonly DependencyProperty IconVisibilityBoldProperty =
DependencyProperty.Register("IconVisibilityBold", typeof(Visibility), typeof(RTFBox),
new PropertyMetadata(Visibility.Hidden), VisibilityValidateCallback);
private static bool VisibilityValidateCallback(object value)
{
Visibility prop = (Visibility) value;
if (prop == Visibility.Hidden || prop == Visibility.Visible)
{
return true;
}
return false;
}
public Visibility IconVisibilityBold
{
get
{
return (Visibility)GetValue(IconVisibilityBoldProperty);
}
set
{
SetValue(IconVisibilityBoldProperty, value);
}
}
Edit: for correct XAML, look for Slugarts answer.
The XAML Entry for this, in this case a ToggleButton, would be
<ToggleButton Visibility="{Binding Path=IconVisibilityBold}" ToolBar.OverflowMode="Never" x:Name="ToolStripButtonBold" Command="EditingCommands.ToggleBold" ToolTip="Bold">
<Image Source="Images\Bold.png" Stretch="None"/>
</ToggleButton>
I've output the Property, it shows as "Hidden" as the Metadata Default Value should imply, but apparently I've done something wrong with the binding. What would I have to write there?
You are trying to binding to a property of the parent control without referencing it, and it won't be set implicitly. You need to set the ElementName in the ToggleButton binding to be the name of the UserControl you are creating (giving it an x:Name property if it doesn't have one already).
<UserControl x:Name="rtfBox">
<ToggleButton Visibility="{Binding ElementName=rtfBox, Path=IconVisibilityBold}" ... />
...
</UserControl>
Also you should follow the previous answers which correctly state that the Visibility property is an enum and not a string.
The ToggleButton's Visibility property requires a value of type System.Windows.Visibility. You need to change your code to use that instead of strings:
public static readonly DependencyProperty IconVisibilityBoldProperty =
DependencyProperty.Register("IconVisibilityBold", typeof(System.Windows.Visibility), typeof(RTFBox));
public System.Windows.Visibility IconVisibilityBold
{
get
{
return (System.Windows.Visibility)GetValue(IconVisibilityBoldProperty);
}
set
{
SetValue(IconVisibilityBoldProperty, value);
}
}
So your property is a string...but it has to be a enumerable:
namespace System.Windows
{
public enum Visibility : byte
{
Visible,
Hidden,
Collapsed,
}
}
You have to bind textbox the datacontext or use it as reference to access the property correctly

Categories