Deriving from Binding class (Silverlight 4.0) - wpf

Using the existing Binding class, we can write,
<TextBox Text="{Binding Email, Mode=TwoWay}"/>
So we can write anything as Email; there is no validity check by Binding itself. I started writing a class BindingMore deriving from Binding so that eventually I could write,
<TextBox Text="{local:BindingMore Email, Validate=SomeMethod, Mode=TwoWay}"/>
Where SomeMethod is some ICommand or delegate which will be triggered to validate the Email . That is my objective, and I've not written that yet.
As of now, I've written just this code,
public class BindingMore : System.Windows.Data.Binding
{
public BindingMore() : base()
{
}
public BindingMore(string path) : base(path)
{
}
}
So, at this stage, BindingMore is exactly equivalent to Binding, yet when I write
<TextBox Text="{local:BindingMore Email, Mode=TwoWay}"/>
It's giving me runtime error. But when I write,
<TextBox Text="{local:BindingMore Path=Email, Mode=TwoWay}"/>
It's working fine. Can anybody tell me why it's giving runtime error in the first case?
Unfortunately, the error is not shown. All it shows is this:
Also, I get the following error message from XAML (even when it builds perfectly and runs (in the second case)):
Type 'local:BindingMore' is used like
a markup extension but does not derive
from MarkupExtension.

Custom Markup Extensions are not supported in Silverlight. Try using an Attached Property approach or a Behavior.

Related

How can add hint from settings in wpf material desgin [duplicate]

In WPF, Can I use binding with values defined in Settings? If this is possible, please provide a sample.
First, you need to add a custom XML namespace that will design the namespace where the settings are defined:
xmlns:properties="clr-namespace:TestSettings.Properties"
Then, in your XAML file, access the default settings instance using the following syntax:
{x:Static properties:Settings.Default}
So here is the final result code:
<ListBox x:Name="lb"
ItemsSource="{Binding Source={x:Static properties:Settings.Default},
Path=Names}" />
Source: WPF - How to bind a control to a property defined in the Settings?
Note: As pointed out by #Daniel and #nabulke, don't forget to set Access Modifier of your settings file to Public and Scope to User
The solution above does work, but I find it quite verbose... you could use a custom markup extension instead, that could be used like this :
<ListBox x:Name="lb" ItemsSource="{my:SettingBinding Names}" />
Here is the code for this extension :
public class SettingBindingExtension : Binding
{
public SettingBindingExtension()
{
Initialize();
}
public SettingBindingExtension(string path)
:base(path)
{
Initialize();
}
private void Initialize()
{
this.Source = WpfApplication1.Properties.Settings.Default;
this.Mode = BindingMode.TwoWay;
}
}
More details here : http://www.thomaslevesque.com/2008/11/18/wpf-binding-to-application-settings-using-a-markup-extension/
#CSharper's answer did not work for my WPF application coded in VB.NET (not C#, unlike apparently 99.999% of other WPF applications), as I got a persistent compiler error complaining that Settings could not be found in the MyApp.Properties namespace, which would not go away even after rebuilding.
What worked instead for me, after much searching online, was to instead use the local XAML namespace created by default in my application's main window XAML file:
<Window
<!-- Snip -->
xmlns:local="clr-namespace:MyApp"
<!-- Snip -->
><!-- Snip --></Window>
...and bind to my settings through it using something like the following (where MyBooleanSetting is a setting I defined in my project properties of type Boolean and scope User, with the default Friend access modifier):
<CheckBox IsChecked="{Binding Source={x:Static local:MySettings.Default}, Path=MyBooleanSetting, Mode=TwoWay}"
Content="This is a bound CheckBox."/>
To ensure the settings are actually saved, be sure to call
MySettings.Default.Save()
...somewhere in your code-behind (such as in the Me.Closing event for your MainWindow.xaml.vb file).
(Credit to this Visual Studio forum post for the inspiration; see the reply by Muhammad Siddiqi.)
Here's how I bind the UserSettings:
Generate a dependency variable by typing propdp and then tab twice.
public UserSettings userSettings
{
get { return (UserSettings)GetValue(userSettingsProperty); }
set { SetValue(userSettingsProperty, value); }
}
public static readonly DependencyProperty userSettingsProperty =
DependencyProperty.Register("userSettings", typeof(UserSettings), typeof(MainWindow), new PropertyMetadata(UserSettings.Default));
Now, you can bind userSettings by:
Value="{Binding userSettings.SomeUserSettingHere, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
And make sure you save UserSettings when you change them or on Exit by:
UserSettings.Default.Save();

Can you use a Binding ValidationRule within 1 line in xaml?

I don't know the correct wording to describe what I'm trying to do here... so I'll just show it.
This xaml I know works:
<TextBox>
<TextBox.Text>
<Binding Path="Location" UpdateSourceTrigger="PropertyChanged">
<Binding.ValidationRules>
<domain:NotEmptyValidationRule ValidatesOnTargetUpdated="True"/>
</Binding.ValidationRules>
</Binding>
</TextBox.Text>
</TextBox>
But this is pretty verbose. I would like to do it in a way similar to this...
<TextBox Text={Binding Path=Location, UpdateSourceTrigger=PropertyChanged,
ValidationRules={domain:NotEmptyValidationRuleMarkup ValidateOnTargetUpdated=True}}"/>
I made a class called NotEmptyValidationRuleMarkup that returns an instance of NotEmptyValidationRule, and it sort-of works. Project builds just fine, it runs just fine, everything works exactly as I expect it to. However, I can no longer view my window in the designer. It gives me an Invalid Markup error because The property "ValidationRules" does not have an accessible setter.. And it's true, ValidationRules does not have a setter. If I try to set ValidationRules through code in C# I get a compile error. But for some reason when I assign it in XAML it actually does build and run just fine. I'm confused. Is there a way I can make this work without jacking up the design view of my window?
Even though the xaml interpreter happens to turn the markup extension into something working, this is not really supported.
See MSDN - Binding Markup Extension
The following are properties of Binding that cannot be set using the Binding markup extension/{Binding} expression form.
...
ValidationRules: the property takes a generic collection of ValidationRule objects. This could be expressed as a property element in a Binding object element, but has no readily available attribute-parsing technique for usage in a Binding expression. See reference topic for ValidationRules.
However, let me suggest a different approach: instead of nesting the custom markup extension in the binding, nest the binding in a custom markup extension:
[ContentProperty("Binding")]
[MarkupExtensionReturnType(typeof(object))]
public class BindingEnhancementMarkup : MarkupExtension
{
public BindingEnhancementMarkup()
{
}
public BindingEnhancementMarkup(Binding binding)
{
Binding = binding;
}
[ConstructorArgument("binding")]
public Binding Binding { get; set; }
public override object ProvideValue(IServiceProvider serviceProvider)
{
Binding.ValidationRules.Add(new NotEmptyValidationRule());
return Binding.ProvideValue(serviceProvider);
}
}
And use as follows:
<TextBox Text="{local:BindingEnhancementMarkup {Binding Path=Location, UpdateSourceTrigger=PropertyChanged}}"/>
Ofcourse, for production you may want to add a few more checks in the markup extension instead of just assuming everything is in place.

Silverlight5 MarkupExtension at Design Time

I have a simple IMarkupExtension as follows:
public class HelloWorldMarkup : IMarkupExtension<string>
{
public string ProvideValue(IServiceProvider serviceProvider)
{
return "Hello World";
}
public override string ToString()
{
return "DesignTime Hello World";
}
}
and my Xaml that uses it like this..
<StackPanel>
<TextBlock Text="{my:HelloWorldMarkup}" />
<HyperlinkButton Content="{my:HelloWorldMarkup}" />
</StackPanel>
At runtime, it all works as expected.
At design time however, the Content of the hyperlink shows the design time values (from ToString), but the TextBlock's Text does not show.
If I leave it like this, my designer will whinge to me for days.. Does anyone have any suggestions on how I can have my Markups display design time data in TextBlock Text?
Many thanks,
try..
<TextBlock DataContext="{my:HelloWorldMarkup}" Text="{Binding}" />
You're halfway on the right track. There's some nice workaround to this ("by design") problem:
Use the interface IMarkupExtension and derive from some control with a content property ( e.g. ContentControl).
Now listen to changes to the "Parent" property (you may have to use some tricky workaround using attached properties). The event callback should then call ProvideValue on its own using a custom simple IProvideValueTarget implementation. The result of ProvideValue must then be assigned to the "Content" property. This does not affect runtime as ProvideValue will be evaluated before the control and works like a charm in design time.
I'm also thinking about installing a Binding on the target property thus reducing the base class to FrameworkElement.
Refer to https://github.com/MrCircuit/XAMLMarkupExtensions and https://github.com/MrCircuit/WPFLocalizationExtension for an example of this process.

EF EntityObject not updating databindings of relationships

I'm using EntityFramework, WPF and MVVM in my application and got some problems with updating the databinding of relationships between EntityObjects. I was able to downsize my problem to only a few lines of XAML and I hope someone can help me as I'm still not very confident with EF and MVVM.
Anyway, here we go with the simplified XAML:
<DatePicker Grid.Row="2" Grid.Column="1"
SelectedDate="{Binding Path=File.SentDate,
StringFormat={}{0:dd/MM/yyyy}, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
VerticalAlignment="Center" IsEnabled="{Binding Path=IsEnabled}"/>
<ComboBox Grid.Row="3" Grid.Column="1" ItemsSource="{Binding Contacts}" DisplayMemberPath="Name"
SelectedItem="{Binding Path=File.Sender, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" IsEditable="True"
VerticalAlignment="Center">
</ComboBox>
<Label Content="{Binding Path=File.SenderId}" Grid.Row="4"/>
<Label Content="{Binding Path=File.Sender.Name}" Grid.Row="5"/>
<Label Content="{Binding Path=File.SentDate}" Grid.Row="6"/>
I'm using the last 3 Labels to test my databinding. Changing the File.SentDate using the DatePicker updates the databinding to the last Label without problem.
Now File is of type EntityObject and has a SenderId property of type GUID. It also has a relationship to my Contacts through the Sender property. Obvisouly, SenderId is the GUID of the corresponding Contact EntityObject which is related to File through the Sender relationship. A File can have only 1 single Sender of type Contact.
Anyway, what happens is that when I select another sender using the combobox, the Label displaying the File.SenderId property get properly updated. However, the one with the File.Sender.Name property i.e. the one using the reléationship does not get updated.
So I'm guessing that there is something special about updating the databinding of relationships in EF.
Can someone please suggest a solution to this?
Unfortunately, the Entity Framework doesn’t notify when an association property changes. That’s the reason why your Binding didn’t work.
The issue is reported to Microsoft: http://connect.microsoft.com/VisualStudio/feedback/details/532257/entity-framework-navigation-properties-don-t-raise-the-propertychanged-event
Another workaround is shown by the BookLibrary sample application of the WPF Application Framework (WAF). The Book class listens to the AssociationChanged event and raises the appropriate PropertyChanged event.
public Book()
{
…
LendToReference.AssociationChanged += LendToReferenceAssociationChanged;
}
private void LendToReferenceAssociationChanged(object sender,
CollectionChangeEventArgs e)
{
// The navigation property LendTo doesn't support the PropertyChanged event.
// We have to raise it ourselves.
OnPropertyChanged("LendTo");
}
Looks like I've found a solution, though to me its more like a workaround. It's not the solution I
would have expected but it works.
The XAML is still the same as above, except for one thing. Instead of binding to File.Sender.Name, I bind to File.SenderName like this:
<Label Content="{Binding Path=File.SenderName}" Grid.Row="4"/>
SenderName in this case is a property of the object File which I added in a partial class like this:
public partial class File
{
public string SenderName
{
get
{
if (this.Sender != null)
{
return this.Sender.Name;
}
return string.Empty;
}
}
protected override void OnPropertyChanged(string property)
{
if (property == "SenderId")
{
OnPropertyChanged("SenderName");
}
base.OnPropertyChanged(property);
}
}
So what happens here is that if the SenderId property is changed, I tell the framework to also update the SenderName property. That's it. Works like a charm. Although I'm still not convinced that this is the way it is supposed to work.
Another workaround if you simply want a name is to overide ToString() for the Sender and bind directly to sender. This workaround is good because most of the time when we are databinding to Property of a Property we do it in order to get a "name" of object set as property value. Also this method works for Database First approach too if you edit tt files to add partial to all class definitions.
So you add a file to contain ToString extensions of your Entites and in it you add something like this:
public partial Contacts
{
public override string ToString()
{
return Name;
}
}
so you can databind
<Label Content="{Binding Path=File.Sender}" Grid.Row="5"/>
Now the databinding will detect if the Sender changes, and when it does it will call ToString to determine what to display.
On the other hand if you need to bind to another non standard property you might have problems. I do remember having success with using DataContext and templates to get around it. You bind to Sender and use DataTemplate to determine what to display.

Bind to a value defined in the Settings

In WPF, Can I use binding with values defined in Settings? If this is possible, please provide a sample.
First, you need to add a custom XML namespace that will design the namespace where the settings are defined:
xmlns:properties="clr-namespace:TestSettings.Properties"
Then, in your XAML file, access the default settings instance using the following syntax:
{x:Static properties:Settings.Default}
So here is the final result code:
<ListBox x:Name="lb"
ItemsSource="{Binding Source={x:Static properties:Settings.Default},
Path=Names}" />
Source: WPF - How to bind a control to a property defined in the Settings?
Note: As pointed out by #Daniel and #nabulke, don't forget to set Access Modifier of your settings file to Public and Scope to User
The solution above does work, but I find it quite verbose... you could use a custom markup extension instead, that could be used like this :
<ListBox x:Name="lb" ItemsSource="{my:SettingBinding Names}" />
Here is the code for this extension :
public class SettingBindingExtension : Binding
{
public SettingBindingExtension()
{
Initialize();
}
public SettingBindingExtension(string path)
:base(path)
{
Initialize();
}
private void Initialize()
{
this.Source = WpfApplication1.Properties.Settings.Default;
this.Mode = BindingMode.TwoWay;
}
}
More details here : http://www.thomaslevesque.com/2008/11/18/wpf-binding-to-application-settings-using-a-markup-extension/
#CSharper's answer did not work for my WPF application coded in VB.NET (not C#, unlike apparently 99.999% of other WPF applications), as I got a persistent compiler error complaining that Settings could not be found in the MyApp.Properties namespace, which would not go away even after rebuilding.
What worked instead for me, after much searching online, was to instead use the local XAML namespace created by default in my application's main window XAML file:
<Window
<!-- Snip -->
xmlns:local="clr-namespace:MyApp"
<!-- Snip -->
><!-- Snip --></Window>
...and bind to my settings through it using something like the following (where MyBooleanSetting is a setting I defined in my project properties of type Boolean and scope User, with the default Friend access modifier):
<CheckBox IsChecked="{Binding Source={x:Static local:MySettings.Default}, Path=MyBooleanSetting, Mode=TwoWay}"
Content="This is a bound CheckBox."/>
To ensure the settings are actually saved, be sure to call
MySettings.Default.Save()
...somewhere in your code-behind (such as in the Me.Closing event for your MainWindow.xaml.vb file).
(Credit to this Visual Studio forum post for the inspiration; see the reply by Muhammad Siddiqi.)
Here's how I bind the UserSettings:
Generate a dependency variable by typing propdp and then tab twice.
public UserSettings userSettings
{
get { return (UserSettings)GetValue(userSettingsProperty); }
set { SetValue(userSettingsProperty, value); }
}
public static readonly DependencyProperty userSettingsProperty =
DependencyProperty.Register("userSettings", typeof(UserSettings), typeof(MainWindow), new PropertyMetadata(UserSettings.Default));
Now, you can bind userSettings by:
Value="{Binding userSettings.SomeUserSettingHere, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
And make sure you save UserSettings when you change them or on Exit by:
UserSettings.Default.Save();

Resources