Change Default Binding/Textbox StringFormat in xaml - wpf

I am creating a form with a lot of textboxes that have text attributes as declared below:
Text="{Binding Path=SomeField, StringFormat='\{0:#,##0.##\}', Mode=TwoWay, ValidatesOnExceptions=true, NotifyOnValidationError=true, Source={StaticResource statementsMainsViewSource}}"
In order to save bulk space & keystrokes, I want to know if there is a way to setup default textboxes in each window using styles (eg somewhat similar to the incorrect code below):
<Style TargetType="{x:Type TextBox}">
<Setter Property="StringFormat" Value="\{0:#,##0.##\}" />
</Style>
Unfortunately, it's the binding stuff that has the property, not the text box, and i don't know how to set a binding style.
Can anyone point me to the correct syntax for changing default binding StringFormats - or can anyone suggest how I can do what I'm trying?
cheers and tia
mcalex

Try this
public class TextBinding:Binding
{
public TextBinding()
{
Mode = BindingMode.TwoWay;
StringFormat = #"\{0:#,##0.##\}";
ValidatesOnExceptions = true;
NotifyOnValidationError = true;
}
}
xmlns:myBinding="clr-namespace:WpfApplication2">
<Grid>
<TextBox Text="{myBinding:TextBinding Path=SomeProperty}"/>
</Grid>
Now you can Bind this way to all Textboxes without setting the above 4 properties of binding they will be automatically applied. I hope this will help you to get an idea.

Related

Set default text for textbox in WPF

I need all my textboxes to have a default text so I have done what is explained in this another post. I have used the solution proposed by Steve Greatrex and marked as accepted.
It is working for me but now I am trying to improve it to use in multiple textboxes as a template but for each of my textboxes I want to set a custom different default text.
As template default text is set to whatever, for example "Your Prompt Here" in above link, then all the textboxes I bind this template will have the same text.
I would like to put a different default text for each of my textboxes so how can I do it using the same controltemplate for all the textboxes?
Under "Windows.Resources" I have created an style that cotains the template indicated in the above post:
<Style x:Key="DefaultText" TargetType="TextBox">
<Setter Property="Template">
<Setter.Value>
<!-- here the controltemplate from the above post -->
</Setter.Value>
</Setter>
</Style>
and I use it in my textboxes in the following way:
<TextBox Style="{StaticResource DefaultText}"/>
Use custom attached property instead of the Tag which has no any specific semantic:
public static class TextBoxHelper
{
public static readonly DependencyProperty DefaultTextProperty = DependencyProperty.RegisterAttached(
"DefaultText",
typeof(string),
typeof(TextBoxHelper));
[AttachedPropertyBrowsableForType(typeof(TextBox))]
public static string GetDefaultText(FrameworkElement element)
{
return (string)element.GetValue(DefaultTextProperty);
}
public static void SetDefaultText(FrameworkElement element, string value)
{
element.SetValue(DefaultTextProperty, value);
}
}
Then you can use it from XAML:
xmlns:helpers="<your_namespace_with_helpers>"
<TextBox helpers:TextBoxHelper.DefaultText="..."/>
Then in your ControlTemplate you can set Text like this:
Text="{Binding Path=(helpers:TextBoxHelper.DefaultText), RelativeSource={RelativeSource TemplatedParent}}"
Although this approach is more verbose than using the Tag property I recommend you to use it because:
If your code will be maintained by someone else it will be quiet unexpected for this person that control relies on Tag property which can contain anything since its type is object.
DefaultText attached property has strict semantic. Anyone can say what it needed for just looking on its name and type.
Rule of thumb is always try to avoid using of properties with undefined semantic.
I have solved it by replacing Text property in textblock within control template by this one:
Text="{TemplateBinding Tag}"
then I call it from any textbox like below:
<TextBox Style="{StaticResource WatermarkedTextBox}"
Tag="Type whatever here" />
You can choose default text for each textbox by specifying the Tag property.
Also, this solution does not require the aero theme.
The solution that Clemens propose in this link also works and it is based on aero theme.

wpf: changing the value of a Dependency Property from viewmodel

I have a custom usercontrol with a custom dependency property of type string. It should be bound twoway to a textbox, which is in a surrounding app, like this:
<Textbox Name="TextBoxInHostApp"/>
<ctrl:MyControl
....
MyString = "{Binding ElementName=TextBoxInHostApp, Path=Text, Mode=TwoWay}"
</ctrl:MyControl>
Problem:
The Control should have the functionality to reveal the value of the dependency property also!
In MyControl, I have a simple string property "Name", which should update the MyString dependeny property value.
So normally, if MyString was not bound to the outside world, you would code in the xaml where the control is used:
<Textbox Name="TextBoxInHostApp"/>
<ctrl:MyControl
....
MyString = "{Binding Name}"
</ctrl:MyControl>
But that's not available in my case.
Attempt 1:
I used a trigger inside the usercontrol xaml:
<UserControl.Style>
<Style>
<Style.Setters>
<Setter Property="local:MyControl.MyString" Value="{Binding Name, UpdateSourceTrigger=PropertyChanged}"/>
</Style.Setters>
</Style>
Now this trigger doesn't work, because the "local value" of the dp is already set here:
MyString = "{Binding ...
if i comment out:
<!--<MyString = "{Binding ...
...the trigger works fine, because the "local value" isn't set.
next attempt:
I try the hard way and code in MyControls viewmodel:
public string Name
{
get
{
return _name;
}
set
{
_name = value;
RaisePropertyChanged("Name");
MyOwningUICtrl.SetValue(MyControl.MyStringProperty, value); // ??
}
}
I have to pass a DependencyObject to call a SetValue, which is unpleasant for MVVM. There must be better solutions for this scenario ?
This is by design
Per WPF's Dependency Property Precedence List, values specified locally within a <Tag> will always take precedence over values specified in a Style or a Trigger
A common problem is setting a value in a <Tag>, then trying to change it in a Style.Trigger. The solution is to set the default value as a <Setter> in the <Style> instead of in the <Tag>, which will then allow the triggered value to take precedence over the styled value
<Style>
<!-- Default Value
<Setter Property="local:MyControl.MyString"
Value="{Binding ElementName=TextBoxInHostApp, Path=Text, Mode=TwoWay}"/>
<Style.Triggers>
<Trigger ...>
<!-- Triggered Value -->
<Setter Property="local:MyControl.MyString"
Value="{Binding Name, UpdateSourceTrigger=PropertyChanged}"/>
</Trigger>
</Style.Setters>
</Style>
But I don't actually see a Trigger defined in your post anywhere, so I'm not really sure if this is your problem or not.
I'm a bit unclear about the exact structure of your code, but keep in mind that a Dependency Property is kind of like a pointer to another value. You can set it to point to TextBox.Text, or to point to DataContext.Name, but not both.
If you want to set both TextBox.Text and MyUserControl.MyString to point to DataContext.Name, then you should bind both properties to "{Binding Name}" like Natxo suggests.
Or if you are trying to bind MyString to UserControl.Name if MyString is not specified in the <ctrl:MyControl ...> tag, then you can try using a PriorityBinding (example here), or just expose the Name property to the outside world to let outside elements use the control like <ctrl:MyControl Name="{Binding ...}" />
If you display in both the Textbox and Mycontrol the same value... why dont you bind them to that property? :
<Textbox Name="TextBoxInHostApp" Text="{Binding Name}"/>
<ctrl:MyControl
....
MyString="{Binding Name}"
</ctrl:MyControl>

WPF Datagrid selecteditem = null in MVVM

I'm trying to work with a datagrid using the MVVM pattern. The problem is that whenever I change the VM property which is binded to SelectedItem to null, the View doesn't "deselect" the currently selected item. This is my binding in xaml:
<DataGrid Grid.Column="0" Grid.Row="0"
ItemsSource="{Binding Path=Users}"
AutoGenerateColumns="False"
CanUserAddRows="False"
IsReadOnly="True"
SelectedItem="{Binding Path=SelectedUser, Mode=TwoWay}">
The SelectedItem binding works from the view to the VM thus in the SelectedUser property I always have the selected object. The problem is that in the VM I'm doing some stuff which sometimes changes the SelectedUser property to null so I would expect the datagrid to deselect the row as well. Instead, it remains selected and if I try to click on the same row, the property doesn't update. If I click on any other row, the property changes as expected.
Is there a way to make the datagrid deselect if it's binded property is set to null? Also I'm looking for a MVVM solution as I don't want to write code behind. I can solve this by writing code behind so don't waste time offering such solutions :)
l.e.: this is my property in the VM:
public RPLUser SelectedUser
{
get
{
return selectedUser;
}
set
{
selectedUser = value;
OnPropertyChanged("SelectedUser");
}
}
Thanks in advance!
I recommend to check the Output Window in visual studio and see if any Binding is failing.
Are you sure when you select something, the selection updates into the SelectedUser property?
Did u put a breakpoint in setter of SelectedUser and see that it is hitting when you select something on the datagrid?
The reasons for this Binding to break could be many ...
SelectedUser is of different type than individual Users.
SelectedUser does not match by reference with any items in Users.
How and where are you setting null?
The following code in my case works perfectly fine...
<tk:DataGrid MaxHeight="200" AutoGenerateColumns="False"
ItemsSource="{Binding}"
SelectedItem="{Binding MySelItem,
ElementName=MyDGSampleWindow,
Mode=TwoWay}"
IsReadOnly="True">
<tk:DataGrid.Columns>
<tk:DataGridTextColumn Header="Key"
Binding="{Binding Key,
Mode=OneWay}"/>
<tk:DataGridTextColumn Header="Value"
Binding="{Binding Value,
Mode=OneWay}"/>
</tk:DataGrid.Columns>
</tk:DataGrid>
When I set MyDGSampleWindow.MySelItem as null, the datagrid propertly deselects. Perhaps you might need to give us more input on how are you actually setting the value as null.
Did you try setting IsSynchronizedWithCurrentItem="True" in the xaml properties for the DataGrid? AFAIK, this will allow you to unselect it by setting the SelectedUser to null.
I cannot test it at the moment, but you could also try to add this in the setter of your property:
set
{
selectedUser = value;
OnPropertyChanged("SelectedUser");
ICollectionView collectionView = CollectionViewSource.GetDefaultView(Users);
collectionView.MoveCurrentTo(selectedUser);
}
(For ICollectionView to do anything, you will need to have IsSynchronizedWithCurrentItem set)
Like I said, I cannot test this right now. Also, the setter of the property is probably not the best place to put it. Maybe create an event handler for the PropertyChangedevent locally and put that logic there.
Let me know if it helps, else I'll see if I can run a short test...
Yeah may need to add the XAML UpdateSourceTrigger to update the UI.
SelectedItem="{Binding SomeProperty, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
The DataGrid will not Deselect it automatically as DataGridRow's IsSelected property should be set to False.
You can do that by Setting a style on DataGrid.. like
<Style x:Key="dataGridRowStyle"
BasedOn="{StaticResource {x:Type WPFToolkit:DataGridRow}}"
TargetType="{x:Type WPFToolkit:DataGridRow}">
<Setter Property="IsSelected" Value="{Binding Path=IsSelected}" />
</Style>
The IsSelected property should be the of the object i.e in your case RPLUser should have a property Isselected
Then before you set the SelectedUser to null... just do SelectedUser.IsSelected=False
And dont forget to attach this style to the DataGridRowStyle in Datagrid
I am using WPFToolkit you can modify the style if you are targetting .NET 4.0

Silverlight DataBinding, avoid BindingExpression Path error on missing properties, hide controls instead

imagine the following simple Models (example for simplicity reasons; in fact, we have MVVM here but it doesn't matter):
public class User {
public string Username { get; set; }
}
public class StackOverflowUser : User {
public int Reputation { get; set; }
}
Now we have a Silverlight UserControl which contains the following Controls (again, this is just an example, stripped down to the core):
<Grid>
<TextBlock Text="Username:" />
<TextBlock Text="{Binding Path=Username}" />
<TextBlock Text="Reputation:" />
<TextBlock Text="{Binding Path=Reputation}" />
</Grid>
Now I'd like this UserControl to be compatible with both Models, User and StackOverflowUser. I might set the UserControl's DataContext to either a User or StackOverflowUser Type:
this.DataContext = new User { Username = "john.doe" };
If set to StackOverflowUser, everything works fine. If set to User, I'm getting a "BindingExpression Path error", because the Property Reputation is missing in the User Model. Which I understand completely.
Is there any way to 1) avoid this
exception and 2) control the
visibility of the controls, collapse
when bound property is not available?
Of course, we prefer an elegant solution, where the problem is solved by tuning the Binding Expression and/or using Converters etc. and avoid tons of code behind if possible.
Thanks in advance for your help and suggestions,
best regards,
Thomas
Unfortunately Silverlight is limited in its polymorphic behavior regarding DataTemplates, I can only think of a workaround:
Give the User class the property Reputation too, but make it meaningless, for example -1. Then apply a style to the reputation TextBlocks:
<Page.Resources>
<Style Key="Reputation">
<Style.Triggers>
<DataTrigger Binding="{Binding Path=Reputation} Value="-1">
<Setter Property="Visibility" Value="Invisible" />
</DataTrigger>
</Style.Triggers>
</Style>
</Page.Resources>
...
<TextBlock Text="Reputation:" Style="{StaticResource Reputation}">
<TextBlock Text="{Binding Path=Reputation}" Style="{StaticResource Reputation}">
You could also try (I can not test this):
giving the User class a new property that identifies its type,
make a second Style for the second TextBlock
bind its DataTrigger to the type identifying property and move the {Binding Path=Reputation} declaration into a Setter:
<Style Key="ReputationContent">
<Style.Triggers>
<DataTrigger Binding="{Binding Path=Type} Value="StackOverflow">
<Setter Property="Visibility" Value="Invisible" />
<Setter Property="Text" Value="{Binding Path=Reputation}" />
</DataTrigger>
</Style.Triggers>
</Style>
But you see, there is no elegant way, it's a shame that DataTemplate do not have a DataType property in Silverlight.
You mentioned you're using MVVM. This is the value of your viewmodel - to shape model data in preparation for the view. The viewmodel could have accessible properties for both username and reputation (and maybe even another bool for binding the visibility). The viewmodel would include all logic on how to fill those properties from either model (User or StackOverflowUser). The view would have no knowledge of a User or StackOverflowUser object, just the viewmodel.
I finally got my problem solved. A co-worker finally implemented a solution including a workaround for WPFs DataTemplates DataType attribute (or generally, a DataTemplateSelector). It's not very pretty (i guess, no workaround is) but it works. Unfortunately, i cannot post any code snippets because its closed-source. But i found some links afterwards, providing a pretty similar solution, like this one: Silverlight: a port of the DataTemplateSelector. If you have a similar problem, this will help you as well. Here or there are more thoughts on this subject.
The actual solution is following Ozan's hints. Unfortunately, his solution is not working so I don't want to mark his comment as the accepted answer but I give at least an upvote.
Thanks!
best regards,
Thomas
I know this has already been answered, but I still think its worth this post. Using reflection you can have a property in your ViewModel that will easily handle Dto objects which only sometimes have the property. Reflection can be expensive though, so weigh that with your decision.
public int? Reputation
{
get
{
var prop = Dto.GetType().GetProperty("Reputation");
return (prop != null)? (int)prop.GetValue(Dto, null) : null;
}
set
{
var prop = Dto.GetType().GetProperty("Reputation");
if(prop !=null) prop.SetValue(Dto,value, null);
}
}

WPF databinding with a user control

I have a wpf user control, which exposes a single custom dependency property. Inside the user control, a textblock binds to the value of the dp. This databinding works in all scenarios except when the data source is an object.
The minimal code necessary to reproduce this is:
this is the main part of the user control
<StackPanel Orientation="Horizontal">
<TextBlock Text="**SimpleUC** UCValue: "/>
<TextBlock Text="{Binding UCValue}"/>
</StackPanel>
and the user control code behind:
public SimpleUC()
{
InitializeComponent();
this.DataContext = this;
}
public string UCValue
{
get { return (string)GetValue(UCValueProperty); }
set { SetValue(UCValueProperty, value); }
}
public static readonly DependencyProperty UCValueProperty =
DependencyProperty.Register("UCValue", typeof(string), typeof(SimpleUC), new UIPropertyMetadata("value not set"));
this is the test window. I imported my project xml namespace as "custom"
<Window.Resources>
<Style TargetType="{x:Type StackPanel}">
<Setter Property="Margin" Value="20"/>
</Style>
</Window.Resources>
<StackPanel>
<StackPanel>
<TextBlock Text="This fails to bind:"/>
<custom:SimpleUC UCValue="{Binding SomeData}"/>
</StackPanel>
<StackPanel>
<TextBlock>The same binding on a regular control like Label</TextBlock>
<Label Content="{Binding SomeData}"/>
</StackPanel>
<Slider x:Name="sld" />
<StackPanel>
<TextBlock>However, binding the UC to another element value, like a slider works</TextBlock>
<custom:SimpleUC UCValue="{Binding ElementName=sld,Path=Value}"/>
</StackPanel>
</StackPanel>
and the test window code behind is:
public TestWindow()
{
InitializeComponent();
this.DataContext = this;
}
//property to bind to
public string SomeData { get { return "Hello S.O."; } }
When I turn on the diagnostic tracing on the TestWindow, it spits out the error "BindingExpression path error:
'SomeData' property not found on 'object' ''SimpleUC' (Name='')' ... "
The binding expression is the same as the one I used in the neighboring label and it worked fine. This behavior seems really bizarre to me. Can anyone shed some light?
You set DataContext of your SimpleUC to itself here
public SimpleUC()
{
InitializeComponent();
this.DataContext = this; // wrong way!
}
so when you use binding here
<custom:SimpleUC UCValue="{Binding SomeData}"/>
it searches property SomeData in control's data context which is set to this object because code in SimpleUC constructor overrides value of DataContext and it is not set to TestWindow object anymore as you expected. That's why your solution works - it doesn't affect DataContext which is inherited from window. Also you can keep this.DataContext = this; but set element where to search property explicitly like this (skipped irrelevant)
<Window ... Name="wnd1">
<custom:SimpleUC UCValue="{Binding SomeData, ElementName=wnd1}"/>
...
But my oppinion is that your variant from the answer looks more convenient to me, setting data context to this is not very good practice.
Hope it helps.
If you must use a UserControl, your
<TextBlock
Text="{Binding RelativeSource={RelativeSource Self},
Path=Parent.Parent.UCValue}"
/>
is an ok way to do it and
<TextBlock
Text="{Binding UCValue,
RelativeSource={RelativeSource FindAncestor,custom:SimpleUC,1}}"
/>
is better because you don't rely on the control hierarchy and possible instantiation order issues.
However I would recommend for this kind of situation that you use "custom controls" instead of "user controls". They take a little bit of getting used to, but they are much more powerful because their XAML is the template itself which means you can use TemplateBinding and {RelativeSource TemplatedParent}.
In any case, DataContext = this; is definitely to be avoided.

Resources