Is there a way to template Binding.Converter and Binding.ValidationRules within a style?
Eg: I have the following textbox:
<TextBox x:Name="DepartTime" Height="23" HorizontalContentAlignment="Left" HorizontalAlignment="Left"
Margin="3" Width="140"
Style="{DynamicResource TimeOfDayTextBox}">
<TextBox.Text>
<!-- Textbox notifies changes when Text is changed, and not focus. -->
<Binding Path="FlightDepartTime" StringFormat="{}{0:hh:mm tt}" >
<Binding.Converter>
<convert:TimeOfDayConverter />
</Binding.Converter>
<Binding.ValidationRules>
<!-- Validation rule set to run when binding target is updated. -->
<validate:ValidateTimeOfDay ValidatesOnTargetUpdated="True" />
</Binding.ValidationRules>
</Binding>
</TextBox.Text>
</TextBox>
.. I can't figure out how to incorporate the Converter and the Validation rule into my TimeOfDayTextBox style.
Many Thanks.
Unfortunately, no. The style could only set the Text property itself to a Binding. It cannot set attributes of the binding. Also, since Binding is not a DependencyObject there is no way to style a binding.
One option you have to make your code more concise is to use a custom MarkupExtension that creates the binding you want:
public class TimeOfDayBinding
: MarkupExtension
{
public PropertyPath Path { get; set; }
public override object ProvideValue(IServiceProvider serviceProvider)
{
var binding = new Binding()
{
Path = Path,
Converter = new TimeOfDayConverter(),
};
binding.ValidationRules.Add(new ValidateTimeOfDay()
{
ValidatesOnTargetUpdated = true,
});
return binding.ProvideValue(serviceProvider);
}
}
Given your control names, you may also want to use a time picker control instead of a TextBox. Check out this question: What is currently the best, free time picker for WPF?
A style can contain only a common set of property which can be applied to multiple controls. In your case, the converter and the validation rule aren't applied to the textbox, but to the content of the binding, so they are specific for a single element and cannot be used in a style.
Related
I have created an AttachedProperty that has a PropertyChangedCallback function, which does some formatting to a TextBlock. But to perform the formatting, the Tag attribute is needed. The Tag itself is bound to the output of a Multiconverter.
But my problem is, that the FNamePropertChangedCallback is executed before the Tag is bound to the output of the Multibinding. Thus Tag still Null, when FNamePropertyChangedCallback is triggered.
Is there any way to influence the order in which the Attributes are bound?
I need to bind Tag before binding FInlineProperty.
public static readonly DependencyProperty FInlinePropertyProperty =
DependencyProperty.RegisterAttached("FInlineProperty", typeof(string), typeof(MainWindow),
new PropertyMetadata(null, FNamePropertyChangedCallback));
<TextBlock local:MainWindow.FInlineProperty="{Binding Name}" TextWrapping="WrapWithOverflow">
<TextBlock.Tag>
<MultiBinding Converter="{StaticResource TupleConverter}">
<Binding />
<Binding ElementName="Window"/>
</MultiBinding>
</TextBlock.Tag>
</TextBlock>
Btw: Does someone know how to write local:MainWindow.FInlineProperty="{Binding Name}" in long form? I tried <TextBlock.local:MainWindow.FInlinse>...</TextBlock.local:MainWindow.FInlinse>, but the compiler complains about it.
Update 25.Feb.2017:
I did what you suggested and bound everything to my Attached Property.
<TextBlock TextWrapping="WrapWithOverflow" >
<local:MainWindow.FInlineProperty>
<MultiBinding Converter="{StaticResource GroupConverter}">
<Binding />
<Binding ElementName="Window" Path="MySetup" />
</MultiBinding>
</local:MainWindow.FInlineProperty>
</TextBlock>
The problem with this is, that it is working the first time when the ListBoxItem is created. But updating properties from MySetup does not re-trigger FNamePropertyChangedCallback.
MySetup.ColorString = "green"; // this does not retrigger the callback
MySetup = MySetup.Copy(); // this does retrigger the callback
So currently only assigning MySetup a new copy of itself (changing the reference) triggers callback function again.
That was the reason, why I bound the name property explicitly.
MySetup.ColorString would trigger, if I bind to it explicitly, but I need to bind to MySetup to have all data, so the question is, how to force the binding to execute again, when the bound object itself (reference) has not changed, but something inside did change?
What you actually want is binding between your AttachedProperty and TextBlock.Tag property, so that AttachedProperty is set when Tag changes. But since you are also binding to Name, so I suggest to use MultiBinding for your AP using Name and Tag bindings, thus not depending upon Tag at all.
Try to set the Tag property before you set the attached property. This means that you should also set the attached property using element syntax:
<TextBlock TextWrapping="WrapWithOverflow">
<TextBlock.Tag>
<MultiBinding Converter="{StaticResource TupleConverter}">
<Binding />
<Binding ElementName="Window"/>
</MultiBinding>
</TextBlock.Tag>
<local:MainWindow.FInlineProperty>
<Binding Path="Name" />
</local:MainWindow.FInlineProperty>
</TextBlock>
I am trying to multibind a formatted double value to a text box. I have a converter which takes in a double and a Formatter object and returns a formatted string to be displayed. The double is bound to a particular data source and the formatter is a property in the view model. The problem I'm having is that I'm unable to bind to the view model property. This is my code in xaml
<StackPanel Grid.Row="0" Grid.Column="1">
<TextBlock HorizontalAlignment="Left" Style="{StaticResource HintDataItemsStyle}">
<TextBlock.Text>
<MultiBinding Converter="{StaticResource FormatConverter}">
<Binding Path="OpenValue" />
<Binding Path="XLabelFormatterY1" />
</MultiBinding>
</TextBlock.Text>
</TextBlock>
This is the property in the view model
private ILabelFormatter _labelFormatterY1;
public ILabelFormatter XLabelFormatterY1
{
get { return _labelFormatterY1; }
set
{
_labelFormatterY1 = value;
OnPropertyChanged("XLabelFormatterY1");
}
}
So, in my converter I'm able to pick up the value for "OpenValue" ,but the runtime is unable to find XLabelFormatterY1. Most of the examples I have seen for multibinding bind to gui components. I'm trying to bind to the view model and would appreciate all help.
Old question but without answer. I beleive that you are looking for this solution. If this answer doesn't work for you, try to explicitly set NotifyOnSourceUpdated="True" in the binding. And also double check if you have set correct AncestorType as wookietomwookie says in his answer.
I have a dialog in my project that the user enters some values in and when he hits OK I add an item to my database. I am using Entity Framework, so my adding to database code is something like this:
TransactionItem _item = new TransactionItem();
_item.DoctorID = (int)cmbDoctor.SelectedValue;
_item.TransactionCategoryID = (int)_dlg.cmbCat.SelectedValue;
_item.TransactionMethodID = (int)_dlg.cmbMethod.SelectedValue;
_item.Amount = int.Parse(_dlg.txtAmount.Text);
_item.DocumentID = _dlg.txtDocNum.Text;
_item.Info = _dlg.txtInfo.Text;
_item.Date = _dlg.dteDate.SelectedDate.ToString();
_db.TransactionItems.Add(_item);
_db.SaveChanges();
But the problem is there is nothing to bind and enable validating. I have tried making an empty object in my window and bind text box to it, but it had its own problems and didn't work as expected. I just want to when users enter values or when he hits OK, check if all of fields are valid (for example one of problems was if the user didn't enter any value, it is still valid even though the stringnotnull validator is enabled, but the most important problem was that it automatically set the textbox's text to null and mark it as a null value).
And I have made my own validator and here is a example of how I implemented them on one of my textboxes:
<TextBox Name="txtAmount" HorizontalAlignment="Left" Height="23" Margin="83,169,0,0" VerticalAlignment="Top" Width="224" Tag="T">
<TextBox.Text>
<Binding Path="myitem" ElementName="myWindow" UpdateSourceTrigger="PropertyChanged">
<Binding.ValidationRules>
<Validators:StringNullValidationRule/>
<Validators:IsNumericValidationRule/>
</Binding.ValidationRules>
</Binding>
</TextBox.Text>
</TextBox>
Why don't you create a property in your viewmodel for each value the user needs to enter, and bind to it? Then you could use these properties when adding an item. For example:
ViewModel:
public int Amount { get; set; }
...
public void AddItem()
{
TransactionItem _item = new TransactionItem();
// ...
_item.Amount = Amount;
}
XAML:
<TextBox Name="txtAmount" HorizontalAlignment="Left" Height="23" Margin="83,169,0,0" VerticalAlignment="Top" Width="224" Tag="T">
<TextBox.Text>
<Binding Path="DataContext.Amount" ElementName="myWindow">
<Binding.ValidationRules>
<Validators:StringNullValidationRule/>
<Validators:IsNumericValidationRule/>
</Binding.ValidationRules>
</Binding>
</TextBox.Text>
</TextBox>
I also recommend having a look at the INotifyDataErrorInfo interface (or the IDataErrorInfo interface if you're using .NET 4.0 or lower) to implement validations.
Use the IDataErrorInfo interface. You can implement it in your ViewModel or your Model class depending on your design. An example of how you can do it is in WPF: Validation made easy with IDataErrorInfo.
And I recommend you read this great Josh Smith article: WPF Apps With The Model-View-ViewModel Design Pattern. There you can see a good example of validation.
I am having trouble getting a custom ValidationRule to fire, when it is associated with an Expander.Header binding. In fact, the only place I can seem to get these custom rules to fire is in a DataGrid.RowValidationRules block...
The expander is defined in my Window XAML file like so;
<Expander Style="{StaticResource ValidatedSecondLevelExpanderStyle}">
<Expander.Header>
<Binding Path="Name" Mode="OneWay" ValidatesOnDataErrors="True" NotifyOnValidationError="True">
<Binding.ValidationRules>
<ValidationRules:BoundObjectIsValid />
</Binding.ValidationRules>
</Binding>
</Expander.Header>
</Expander>
The bound property 'Name' is displayed correctly, but the validation rule 'BoundObjectIsValid' does not get invoked. Is this possible, and if so, what am I missing?
I know that I could alternately implement IDataErrorInfo on the bound object, however the object can't sensibly validate itself without some context that is provided by other parts of the system. Refactoring is possible, but I'd love to get the ValidationRules to work first!
Refer to the msdn.
The binding engine checks each ValidationRule that is associated with a binding every time it transfers an input value, which is the binding target property value, to the binding source property.
So here in your case, you don't have an inpurt value being transfered to the source property since your Expander.header is not a control which you can use to input values.
Edit: But there is a property named ValidatesOnTargetUpdated' in the ValidationRule. When setting it to true, the validationrule will be applied when the target property is updated
I'm relatively new to DataBinding and just reading into it.
What I want to do is the following:
I have a StackPanel with a number of child controls:
<StackPanel Orientation="Horizontal">
<TextBox x:Name="textbox1" Width="100">1</TextBox>
<TextBox x:Name="textbox2" Width="100">2</TextBox>
<TextBox x:Name="textbox3" Width="100">3</TextBox>
</StackPanel>
The visibility property of the textboxes can be changed by code.
Now, if all TextBoxes are set to Visibility=Collapsed, i also want StackPanel.Visibility set to Collapsed, but if one or more TextBoxes are shown (Visibility=Visible), StackPanel.Visibility should also be set to Visible.
Can this be achieved with a simple DataBinding or do I have to implement this functionality in C# code?
I can not think of a way to do this directly through databinding.
Personally I would have a view model behind the view, and set the views DataContext to the view model.
In the view model I would then have a property telling the view if all the textboxes are collapsed. That property would be set by code. Then bind the stackpanel visibility to that property.
(The property must either be a dependancy property, or the view model must implement INotifyPropertyChanged for the view to automatically update)
Have you considered setting the visibility of the TextBoxes to Hidden? This will "hide" the space that is assigned for the TextBoxes. Assuming their are no other controls in the StackPanel, then it will not be visible.
Of course, this solution may make some naive assumptions about your implementation.
If you need the more complex scenario, I would attempt the following:
Note: This is psuedocode - may not compile..
1) Use a MultiBinding
<StackPanel>
<StackPanel.Visibility Converter={StaticResource visibilityConverter}>
<MultiBinding.Bindings>
<Binding ElementName="textBox1" Path="Visibility" />
<Binding ElementName="textBox2" Path="Visibility" />
<Binding ElementName="textBox3" Path="Visibility" />
</MultiBinding.Bindings>
</StackPanel.Visibility>
</StackPanel>
2) Declare the Converter
<Window.Resources>
<local:VisibilityConverter x:Key="visibilityConverter" />
</Window.Resources>
3) Define the Converter
public class VisibilityConverter : IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
Visibility text1Vis = (Visibility)values[0];
Visibility text2Vis = (Visibility)values[1];
Visibility text3Vis = (Visibility)values[2];
if (text1Vis == text2Vis == text3Vis == Visibility.Collapsed)
return Visibility.Collapsed;
return Visibility.Visible;
}
}