Please guide me how to code tooltip in xaml - wpf

I never used WPF , tooltip before I am assigned to implement textbox that receieve number in range 1-999 if it out of this range tooltip will be shown near the textbox and textbox is changedto red border while input is out of range. pls guide me i try to bind xaml and function in C# but nothing happen
<TextBox HorizontalAlignment="Left" Height="31" TextWrapping="Wrap" VerticalAlignment="Top" Width="276" Margin="73,71,0,0" PreviewTextInput="PreviewTextInput" PreviewKeyDown="TextboxPreviewKeydown" >
<TextBox.Text>
<Binding Path="Number">
<Binding.ValidationRules>
<ExceptionValidationRule />
</Binding.ValidationRules>
</Binding>
</TextBox.Text>
</TextBox>
and my C#
public int Number
{
get { return num; }
set
{
num = value;
if (value <= 0 || value >999)
{
throw new ApplicationException("Out of range");
}
}

In simplest way you can do this:
<TextBox Text="{Binding}">
<TextBox.ToolTip>
<TextBlock Text="{Binding }"/> // Here you can bind to property or give static value to show in tooltip
</TextBox.ToolTip>
</TextBox>
Thanks

What you need is to build a validation rule
You create a class ("MyValidation" for example) that extends "ValidationRule" and you implement it
Then in your xaml, you can do this
<TextBox>
<TextBox.Text>
<Binding...>
<Binding.ValidationRules>
<local:MyValidation/>
</Binding.ValidationRules>
</Binding>
</TextBox.Text>
</TextBox>

Related

Bind Multibinding Textbox in WPF MVVM

I have 3 TextBoxes bind with my class(Transaction) properties like this
<TextBox Text="{Binding Path=Transaction.Bills100,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}" Name="bills100" Grid.Column="2" Grid.Row="1" Margin="7"></TextBox>
<TextBox Text="{Binding Path=Transaction.Bill50,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}" Name="bills50" Grid.Column="2" Grid.Row="2" Margin="7"></TextBox>
<TextBox Text="{Binding Path=Transaction.Bill20,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}" Name="bills20" Grid.Column="2" Grid.Row="3" Margin="7"></TextBox>
Also I have another TextBox where I have done multibinding and done addition of the first three Textboxes like
<TextBox Grid.Column="2" IsReadOnly="True" Grid.Row="7" Grid.ColumnSpan="2" Margin="7" Name="TotalBills">
<TextBox.Text>
<MultiBinding Converter="{ikriv:MathConverter}" ConverterParameter="x+y+z" Mode="TwoWay">
<Binding Path="Text" ElementName="bills100" />
<Binding Path="Text" ElementName="bills50" />
<Binding Path="Text" ElementName="bills20" />
</MultiBinding>
</TextBox.Text>
</TextBox>
I want to bind this multibinding textbox with my class(Transaction) with property as Transaction.Total like my first three textboxes but it shows error
Property text is set more than once
Actually we cannot get the value of a two-way binding from one property and then set the value of another property.
Finally I came with a solution like this
In my Class Transaction
private double _totalBills;
public double TotalBills
{
get { return _totalBills; }
set { _totalBills= value; Notify("TotalBills"); }
}
In XAML(Instead of Multibinding)
<TextBox Text="{Binding Path=Transaction.TotalBills,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}" Grid.Column="2" IsReadOnly="True" Grid.Row="7" Grid.ColumnSpan="2" Margin="7" Name="TotalBills"/>
My ViewModel
public class MainViewModel: INotifyPropertyChanged
{
private Transaction _transactionDetails;
public MainViewModel()
{
Transaction= new Transaction();
_transactionDetails.PropertyChanged += _transactionDetails_PropertyChanged;
}
private void _transactionDetails_PropertyChanged(object sender,PropertyChangedEventArgs e)
{
switch (e.PropertyName)
{
case "TotalBills":
_calculate(); //My method for calculation
break;
}
}
}

How do I properly implement Textbox Validation

I am new to WPF and creating an application which as few text boxes. Text boxes are bound to some source using MVVM. Now when I click on save button, it should fire validation for all empty text boxes and save event should not be fired. How can I achieve this in WPF.
I have written validater but it is not called. See my code below:
<TextBox Width="250" Grid.Row="0" Grid.Column="1" Margin="10">
<TextBox.Text>
<Binding Path="ContinuousModel.FileName" ValidatesOnDataErrors="True" NotifyOnValidationError="True" Mode="TwoWay" ValidatesOnExceptions="True">
<Binding.ValidationRules>
<validate:RequiredFieldValidatation />
</Binding.ValidationRules>
</Binding>
</TextBox.Text>
</TextBox>
public class RequiredFieldValidatation:ValidationRule
{
public override ValidationResult Validate(object value, CultureInfo cultureInfo)
{
if (value == null || value.ToString() == string.Empty)
return new ValidationResult(false, "Value cannot be empty");
return ValidationResult.ValidResult;
}
}
The above validator is getting called only if some text is written then change focus then come back and remove value and then change focus.
NOTE: Using MVVM and datatemplates, I am loading varios user controls and those controls are bound to viewmodel. Save button is in different user control so I cannot validate all text boxes manually on save button click event.
Add the UpdateSourceTrigger property to your binding;
<Binding Path="ContinuousModel.FileName" UpdateSourceTrigger=PropertyChanged ValidatesOnDataErrors="True" NotifyOnValidationError="True" Mode="TwoWay" ValidatesOnExceptions="True">
<Binding.ValidationRules>
<validate:RequiredFieldValidatation />
</Binding.ValidationRules>
</Binding>

Validate two TextBox.Text values to be distinct

Grab two TextBox and say that you need to validate that their content are distinct strings.
Example :
Correct result : prefix1, prefix2
Incorrect result : prefix1, prefix1
To do that task I thought about using a MultiBinding but two problems arises then :
Where should it be placed ? currently it is on a dummy TextBox
Even using that dummy TextBox, the ValidationRule is never called
Not sure whether this approach is correct, how would you do that ?
<MultiBinding Converter="{StaticResource MyConverter}">
<Binding Path="GradientPrefix"
Source="{StaticResource Indices}"
UpdateSourceTrigger="PropertyChanged" />
<Binding Path="ColorPrefix"
Source="{StaticResource Indices}"
UpdateSourceTrigger="PropertyChanged" />
<MultiBinding.ValidationRules>
<gpl2Xaml:DistinctStringValidationRule />
</MultiBinding.ValidationRules>
</MultiBinding>
Here's the solution using a BindingGroup !
Error at BindingGroup level :
Error at BindingGroup and field levels :
No errors :
Here's the code :
<Window>
<Window.Resources>
<gpl2Xaml:Indices x:Key="Indices"
ColorIndex="1"
ColorPrefix="MyColor"
GradientIndex="1"
GradientPrefix="MyColor" />
</Window.Resources>
<Grid DataContext="{StaticResource Indices}"
Style="{StaticResource gridInError}"
Validation.ErrorTemplate="{StaticResource validationTemplate}">
<Grid.BindingGroup>
<BindingGroup>
<BindingGroup.ValidationRules>
<gpl2Xaml:DistinctValidationRule />
</BindingGroup.ValidationRules>
</BindingGroup>
</Grid.BindingGroup>
<TextBox x:Name="TextBoxGradientPrefix"
Style="{StaticResource textBoxInError}"
TextChanged="TextBoxGradientPrefix_OnTextChanged"
Validation.ErrorTemplate="{StaticResource validationTemplate}">
<Binding Path="GradientPrefix"
Source="{StaticResource Indices}"
UpdateSourceTrigger="PropertyChanged">
<Binding.ValidationRules>
<gpl2Xaml:StringValidationRule />
</Binding.ValidationRules>
</Binding>
</TextBox>
<TextBox x:Name="TextBoxColorPrefix"
Style="{StaticResource textBoxInError}"
TextChanged="TextBoxColorPrefix_OnTextChanged"
Validation.ErrorTemplate="{StaticResource validationTemplate}">
<Binding Path="ColorPrefix"
Source="{StaticResource Indices}"
UpdateSourceTrigger="PropertyChanged">
<Binding.ValidationRules>
<gpl2Xaml:StringValidationRule />
</Binding.ValidationRules>
</Binding>
</TextBox>
</Grid>
</Window>
Extra code to trigger validation every time :
private void TextBoxGradientPrefix_OnTextChanged(object sender, TextChangedEventArgs e)
{
grid.BindingGroup.CommitEdit();
}
private void TextBoxColorPrefix_OnTextChanged(object sender, TextChangedEventArgs e)
{
grid.BindingGroup.CommitEdit();
}
And the validation rule :
public class DistinctValidationRule : ValidationRule
{
public override ValidationResult Validate(object value, CultureInfo cultureInfo)
{
var bindingGroup = value as BindingGroup;
if (bindingGroup == null) return new ValidationResult(false, "Not a BindingGroup");
var o = bindingGroup.Items[0] as Indices;
if (o == null) return new ValidationResult(false, "Not an Indices");
if (o.ColorPrefix == o.GradientPrefix)
return new ValidationResult(false, "Color prefix and Gradient prefix must be distinct.");
return new ValidationResult(true, null);
}
}

Stop ValidationRule if ComboBox is Collapsed

I have two ComboBox's - cbo_client_pay_method & cbo_terms
One of the cbo_client_pay_method items (On Account) requires cbo_terms (30 days etc...) to be Visible else its Collapsed, I have this functionality already set up in cbo_payment_type_SelectionChanged event.
I have implemented a validationRule that test if the cbo's are not null && if selectedValue <0 (something is selected), this works ok.
This all works great unless the cbo's are collapsed, the validation still fires!
Can I halt the validationRule if the element is collapsed?
<StackPanel Name="sp_account" Orientation="Horizontal" VerticalAlignment="Center">
<Label Content="Payment" Style="{StaticResource formLabel}"/>
<Grid>
<ComboBox Name="cbo_client_pay_method" Style="{StaticResource reminder_cbo}" SelectionChanged="cbo_client_payMethod_SelectionChanged" Validation.ErrorTemplate="{StaticResource validationTemplate}">
<ComboBox.SelectedValue>
<Binding Path="client_payment_type_id" Mode="TwoWay" UpdateSourceTrigger="PropertyChanged" ValidatesOnDataErrors="True">
<Binding.ValidationRules>
<local:ValidCbo ErrorMessage="Select A Payment Type" />
</Binding.ValidationRules>
</Binding>
</ComboBox.SelectedValue>
</ComboBox>
<TextBlock Name="txtSelectPayMethod" Text="Please Select A Payment Method..." Style="{StaticResource cbo_overlay}" />
</Grid>
</StackPanel>
<StackPanel Name="sp_terms" Orientation="Horizontal" VerticalAlignment="Center">
<Label Content="Terms" Style="{StaticResource formLabel}"/>
<Grid>
<ComboBox Name="cbo_terms" Style="{StaticResource reminder_cbo}" SelectionChanged="cbo_terms_SelectionChanged" Validation.ErrorTemplate="{StaticResource validationTemplate}">
<ComboBox.SelectedValue>
<Binding Path="terms_id" Mode="TwoWay" UpdateSourceTrigger="PropertyChanged" ValidatesOnDataErrors="True">
<Binding.ValidationRules>
<local:ValidCbo ErrorMessage="Select Payment Terms" />
</Binding.ValidationRules>
</Binding>
</ComboBox.SelectedValue>
</ComboBox>
<TextBlock Name="txtSelectTerms" Text="Please Select Payment Terms..." Style="{StaticResource cbo_overlay}" />
</Grid>
</StackPanel>
public class ValidCbo : ValidationRule
{
private string _errorMessage;
public string ErrorMessage
{
get { return _errorMessage; }
set { _errorMessage = value; }
}
public override ValidationResult Validate(object value, CultureInfo cultureInfo)
{
//if (this.ErrorMessage.Contains("Master") |)
if (value == null )
{
// value = null
return new ValidationResult(false, this.ErrorMessage);
}
else
{
// Not null
int selectedValue = (int)value;
if (selectedValue < 0)
{
return new ValidationResult(false, this.ErrorMessage);
}
else
{
return ValidationResult.ValidResult;
}
}
}
}
You could apply a style which only binds the value if the ComboBox is visible:
<ComboBox.Style>
<Style TargetType="{x:Type ComboBox}">
<Style.Triggers>
<Trigger Property="Visibility" Value="Visible">
<Setter Property="SelectedValue">
<Setter.Value>
<Binding Path="terms_id" Mode="TwoWay" UpdateSourceTrigger="PropertyChanged" ValidatesOnDataErrors="True">
<Binding.ValidationRules>
<local:ValidCbo ErrorMessage="Select Payment Terms" />
</Binding.ValidationRules>
</Binding>
</Setter.Value>
</Setter>
</Trigger>
</Style.Triggers>
</Style>
</ComboBox.Style>
You must not set the SelectedValue in the ComboBox itself or it will overwrite the style though.

WPF: binding to a dependency property

I am following a tutorial here. which shows a basic example on how to bind to a dependency property.
<Binding ElementName="This" Path="IPAddress" UpdateSourceTrigger="PropertyChanged">
where "This" is the name of the current window:
<Window x:Class="SOTCBindingValidation.Window1" x:Name="This"
whenever i try to do something like this, i keep getting the same error:
Cannot find source for binding with reference 'ElementName=GridControlControl1'. BindingExpression:Path=IPAddress; DataItem=null; target element is 'TextBox' (Name='AddressBox'); target property is 'Text' (type 'String')
my code:
<UserControl x:Class="WpfGridtest.GridControl" x:Name="GridControlControl1" ... />
<TextBox x:Name="AddressBox">
<TextBox.Text>
<Binding ElementName="GridControlControl1" Path="IPAddress" UpdateSourceTrigger="PropertyChanged">
</Binding>
</TextBox.Text>
</TextBox>
codebehind:
partial class GridControl : UserControl
public static readonly DependencyProperty IPAddressProperty = DependencyProperty.Register("IPAddress", typeof(string), typeof(GridControl), new UIPropertyMetadata("1.1.1.1"));
public string IPAddress
{
get { return (string)GetValue(IPAddressProperty); }
set { SetValue(IPAddressProperty, value); }
}
it's almost like something changed in .Net 4.0?
It depends on what you want. I'll try to offer a complete answer. One way to guarantee better success with this syntax is to use VS2010's XAML Binding Builder, which is how I assembled the syntax you're about to see.
If you want an element of the UserControl to display your IPAddress dependency property, (which looks like it's defined correctly to me), use this syntax within the body of the UserControl's markup:
<TextBlock Text="{Binding RelativeSource={RelativeSource FindAncestor,
AncestorType={x:Type my:GridControl},
AncestorLevel=1},
Path=IPAddress}" />
Most XAML binding examples use this syntax rather than the more verbose hierarchical XML:
<TextBlock>
<TextBlock.Text>
<Binding Path="IPAddress">
<Binding.RelativeSource>
<RelativeSource Mode="FindAncestor"
AncestorType="{x:Type my:GridControl}"
AncestorLevel="1"
/>
</Binding.RelativeSource>
</Binding>
</TextBlock.Text>
</TextBlock>
...but both kinds of syntax produce the same results. Note here that the AncestorType is the class name of your UserControl, not the x:Name you would supply when using the UserControl in other markup.
Suppose you have a UI element in markup outside your UserControl, and you want access to your DependencyProperty for that other control. The markup looks something like this:
<my:GridControl
x:Name="GridControl1" IPAddress="192.168.1.1" />
<TextBox Text="{Binding ElementName=GridControl1, Path=IPAddress}"/>
Or, alternatively, this:
<TextBox>
<TextBox.Text>
<Binding ElementName="GridControl1" Path="IPAddress"/>
</TextBox.Text>
</TextBox>
Note here that this time you're using the x:Name property of the GridControl rather than the class name, and that you refer to it as an ElementName, and not an "Ancestor". In both cases, though, the Path is the declared name of the DependencyProperty you defined.
Try using RelativeSource:
<TextBox>
<TextBox.Text>
<Binding Path="IPAddress">
<Binding.RelativeSource>
<RelativeSource
Mode="FindAncestor"
AncestorType="{x:Type UserControl}"
AncestorLevel="1"
/>
</Binding.RelativeSource>
</Binding>
</TextBox.Text>
</TextBox>
Instead of {x:Type UserControl}you could insert your actual type there, i.e.:
<TextBox>
<TextBox.Text>
<Binding Path="IPAddress">
<Binding.RelativeSource>
<RelativeSource xmlns:my="clr-namespace:WpfGridtest"
Mode="FindAncestor"
AncestorType="{x:Type my:GridControl}"
AncestorLevel="1"
/>
</Binding.RelativeSource>
</Binding>
</TextBox.Text>
</TextBox>

Resources