I have a converter that takes in a bool and will return A or B depending on if it was true or false. The converter picks the right value depending on what the bool is, but only at start, if i change the bool at runtime the converter does not update.
Basically, i have a User Control that has a button in it, this button toggles the "IsOpen" property, this works. But i have a multibinder who binds IsOpen to Image (of button) which will toggle the image depending on IsOpen. But it is not updating, only keeps the value at start. (IsOpen does toggle on click, that's not the problem)
My User Control where i do the multibinding:
<v:IconButton ColorPalette="{StaticResource MilkySolid}" ColorPaletteFore="{StaticResource BlackToBrightPalette}" IconMargin="0" Content="" VerticalAlignment="Top" Margin="0" HorizontalAlignment="Left" FontSize="1" Height="26" IconWidth="26" Click="IconButton_Click">
<v:IconButton.Image>
<MultiBinding Converter="{StaticResource AorBConverter}">
<Binding Path="IsOpen"/>
<Binding Source="{StaticResource collapseBTN}"/>
<Binding Source="{StaticResource expandBTN}"/>
</MultiBinding>
</v:IconButton.Image>
</v:IconButton>
CodeBehind (this part works)
private void IconButton_Click(object sender, RoutedEventArgs e)
{
IsOpen = !IsOpen;
}
public bool IsOpen
{
get { return (bool)GetValue(IsOpenProperty); }
set { SetValue(IsOpenProperty, value); }
}
public static readonly DependencyProperty IsOpenProperty =
DependencyProperty.Register("IsOpen", typeof(bool),
typeof(ParamNodeV), new PropertyMetadata(false));
Viewmodel for the user control (this also works)
public bool IsOpen
{
get { return isOpen; }
set
{
isOpen = value;
OnPropertyChanged(nameof(IsOpen));
}
}
So, like i said, the converter chooses the right image depedning on the bool value. But it does not update if i update the bool value at runtime.
And if you will ask me why im not just using a trigger: I'm trying to change the image on a CustomControl (IconButton) from my UserControl (ParamNodeV), and i don't know how to access the properties of IconButton from ParamNodeV, without completely overriding the style/template. So either if someone helps me with my converter or helps me on how to navigate to the Image property of IconButton from UserControl without having to override the style/template
The expression
<Binding Path="IsOpen"/>
takes the current DataContext as source object.
In the code behind you are apparently changing the IsOpen property of the UserControl - which should be a different object.
The Binding should therefore use that property as source, i.e. use the UserControl as source object:
<Binding Path="IsOpen" RelativeSource="{RelativeSource AncestorType=UserControl}"/>
Related
I am working on custom control.
I have three dependency property as mentioned below.
Now depending on the control height, width and a range provided by the user, I have to calculate a value and
display it in the custom control.
I am trying to use a multibinding where I can bind all these three values and my multivalue converter will do some calculation on this
and returns me the appropriate value.
The problem is I do not know to bind this value in the styles as multivalue converter binding.
Dependency Properties:
public static readonly DependencyProperty ControlHeightProperty =
DependencyProperty.Register("ControlHeight", typeof(double), typeof
(TestControl), new PropertyMetadata(150D));
public double ControlHeight
{
get { return (double)GetValue(ControlHeightProperty); }
set { SetValue(ControlHeightProperty, value); }
}
public static readonly DependencyProperty ControlWidthProperty =
DependencyProperty.Register("ControlWidth", typeof (double), typeof
(TestControl), new PropertyMetadata(default(double)));
public double ControlWidth
{
get { return (double) GetValue(ControlWidthProperty); }
set { SetValue(ControlWidthProperty, value); }
}
public static readonly DependencyProperty RangeProperty =
DependencyProperty.Register("Range", typeof (double), typeof
(TestControl), new PropertyMetadata(default(double)));
public double Range
{
get { return (double) GetValue(RangeProperty); }
set { SetValue(RangeProperty, value); }
}
Style (I have not written the binding): If the properties are available in the same style than I can do it
using ElementName binding. But in this case atlease may be for height and width it is possible. But Range is a direct dependency property
which I have to bind in my style (I mean there is no way I can do an ElementName binding)
<TextBlock Grid.Row="1">
<TextBlock.Text>
<MultiBinding Converter="{StaticResource
CalculateConverter}">
<Binding Path=""></Binding>
<Binding Path=""></Binding>
<Binding Path=""></Binding>
</MultiBinding>
</TextBlock.Text>
</TextBlock>
Can someone help me?
Thanks & Regards,
You can do it using RelativeSource.
<Binding Path="Range" RelativeSource="{RelativeSource AncestorType=UserControl, Mode=FindAncestor}"></Binding>
I have a little problem here. I've created custom TreeView using RadTreeView. It all works nice, but I've encountered an obstacle. I've set DependencyProperty for SelectedItem in TreeView. I nest my control in View, bind property to SelectedItem in TwoWay mode, but bound property won't update, it's null all the time, despite DependencyProperty value being set.
Here's tree xaml:
<Grid xmlns='http://schemas.microsoft.com/winfx/2006/xaml/presentation'
xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml'
xmlns:sdk='http://schemas.microsoft.com/winfx/2006/xaml/presentation/sdk'
xmlns:telerik='http://schemas.telerik.com/2008/xaml/presentation' x:Name='this' >
<Grid.Resources>
<DataTemplate x:Key='ChildTemplate'>
<TextBlock Text='{Binding Path=ChildPath}' Margin='5,0' />
</DataTemplate>
<telerik:HierarchicalDataTemplate x:Key='NameTemplate' ItemsSource='{Binding ChildrenCollectionPath}' ItemTemplate='{StaticResource ChildTemplate}'>
<TextBlock Text='{Binding Path=ParentPath }' Padding='7'/>
</telerik:HierarchicalDataTemplate>
</Grid.Resources>
<telerik:RadTreeView x:Name='rtvTreeView' Padding='5' BorderThickness='0' IsEditable='False' IsLineEnabled='True' IsExpandOnDblClickEnabled='False' ItemTemplate='{StaticResource NameTemplate}' />
</Grid>
Below is way I nest the control in View:
<windows:TreeViewReuse CollectionSource="{Binding SitesCollectionWithAddress}" ParentPath="Napis" Grid.Column="0" BorderThickness="2" SelectedItemD="{Binding SelectedSide, ElementName=this, UpdateSourceTrigger=Explicit, Mode=TwoWay}" ChildPath="FullAddress" ChildrenCollectionPath="AdresyStrony" BorderBrush="Red" DoubleClickCommand="{Binding TreeViewDoubleClick}">
</windows:TreeViewReuse>
And here's Tree's code behind in parts:
public partial class TreeViewReuse : UserControl
{
static Telerik.Windows.FrameworkPropertyMetadata propertyMetaData = new Telerik.Windows.FrameworkPropertyMetadata(null,
Telerik.Windows.FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, new PropertyChangedCallback(SelectedItemChangedCallback));
public object SelectedItemD
{
get { return GetValue(SelectedItemDProperty); }
set { SetValue(SelectedItemDProperty, value); }
}
public static readonly DependencyProperty SelectedItemDProperty =
DependencyProperty.Register("SelectedItemD", typeof(object), typeof(TreeViewReuse), propertyMetaData);
public TreeViewReuse()
{
InitializeComponent();
Loaded += new RoutedEventHandler(TreeViewReuse_Loaded);
}
void treeView_SelectionChanged(object sender, Telerik.Windows.Controls.SelectionChangedEventArgs e)
{
SelectedItemD = _treeView.SelectedItem;
}
static private void SelectedItemChangedCallback(DependencyObject dp, DependencyPropertyChangedEventArgs e)
{
}
Does anyone have an idea why property bound to SelectedItemD does not update? I don't care about setting tree's selected item from it, I only want to set it to selected item.
Here's property:
public StronaSprawy SelectedSide
{
get
{
return _selectedSide;
}
set
{
_selectedSide = value;
}
}
Your Dependency Property looks fine.. all except for that Telerik.Windows.FrameworkPropertyMetadata instance.
Silverlight does not support setting meta data options, so I cant think how the Telerik implementation will achieve that. It is possible that Telerik have their own DP implementation, or even that this type of property meta data only works with their controls.
Try using the standard System.Windows.PropertyMetaData type instead and see if that works for you.
In MainWindow class I have checkbox that controls property used by many objects like grids, listviews, etc in UserControls
<CheckBox Content="Show objects ID" Name="showID" IsChecked="False" />
than there is property defined,
public Visibility ShowObjectIDasVisibility
{
get { return showID.IsChecked.Equals(true) ? Visibility.Visible : Visibility.Collapsed; }
}
I have some more like this to return boolean, width depending on what should be used on target control.
I managed to bind controls located in UserControl objects to use this property like this:
<TextBlock Visibility="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}}, Path=ShowObjectIDasVisibility}" />
But it works only ones, while creating this TextBlock, than I can toggle checkbox as many times I like, and the TextBlock will stay visible or not depending on first value.
How should I do this properly? Thanks.
Instead of INotifyPropertyChanged interface you can use DependencyProperty:
public Visibility ShowObjectIDasVisibility
{
get { return (Visibility)GetValue(ShowObjectIDasVisibilityProperty); }
set { SetValue(ShowObjectIDasVisibilityProperty, value); }
}
public static readonly DependencyProperty ShowObjectIDasVisibilityProperty =
DependencyProperty.Register("ShowObjectIDasVisibility", typeof(Visibility), typeof(MainWindow), new PropertyMetadata(Visibility.Collapsed));
Now, to show/hide your TextBlock you need to change ShowObjectIDasVisibility value.
For example, you can do it by adding to checkbox Click="OnShowID_Click and in code behind
private void OnShowID_Click(object sender, RoutedEventArgs e)
{
ShowObjectIDasVisibility = ShowObjectIDasVisibility == System.Windows.Visibility.Visible ? System.Windows.Visibility.Collapsed : System.Windows.Visibility.Visible;
}
if your binding is correct. you just need to make sure that your code class is implementing INotifyPropertyChanged interface in class binded to view and you are raising RaisePropertyChanged event in every checkbox state change. For more details look at example here.
What's wrong with these code, the Validation.Error is never fired whereas I setthe and the NotifyOnValidationError property to True. So, the method "Grid_Error(object sender, ValidationErrorEventArgs e)" is never executed, but I don't know why :(
<Window xmlns:my="clr-namespace:WpfDigitalClock;assembly=WpfDigitalClock" x:Class="WpfApplication1.MainWindow"
xmlns:local="clr-namespace:WpfApplication1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">
<Window.Resources>
<local:Names x:Key="MyNames" />
</Window.Resources>
<Grid Validation.Error="Grid_Error">
<TextBox Height="21" Margin="12,62,0,0" Name="TextBox1" VerticalAlignment="Top" HorizontalAlignment="Left" Width="120">
<TextBox.Text>
<Binding Source="{StaticResource MyNames}" Path="FirstName" NotifyOnValidationError="True">
<Binding.ValidationRules>
<local:StringValidator />
</Binding.ValidationRules>
</Binding>
</TextBox.Text>
</TextBox>
<TextBox Height="21" HorizontalAlignment="Right" Margin="0,62,12,0" Name="TextBox2" VerticalAlignment="Top" Width="120" >
<TextBox.Text>
<Binding Source="{StaticResource MyNames}" Path="LastName" NotifyOnValidationError="True">
<Binding.ValidationRules>
<local:StringValidator />
</Binding.ValidationRules>
</Binding>
</TextBox.Text>
</TextBox>
<Button HorizontalAlignment="Left" Margin="35,122,0,116" Name="Button1" Width="75" Click="Button1_Click">Back</Button>
<Button HorizontalAlignment="Right" Margin="0,122,34,117" Name="Button2" Width="75" Click="Button2_Click">Forward</Button>
<Button Height="22" Margin="101,0,101,56" Name="Button3" VerticalAlignment="Bottom" Click="Button3_Click">Add</Button>
</Grid>
in the Window1.xaml.cs file :
public class StringValidator : ValidationRule
{
public override ValidationResult Validate(object value,
System.Globalization.CultureInfo cultureinfo)
{
string aString = value.ToString();
if (aString == "")
return new ValidationResult(false, "String cannot be null");
return new ValidationResult(true, null);
}
}
private void Grid_Error(object sender, ValidationErrorEventArgs e)
{
if(e.Action == ValidationErrorEventAction.Added)
MessageBox.Show(e.Error.ErrorContent.ToString());
}
Thank you for your help !
EDIT :
Here my Names Class :
class Names : ObservableCollection<Name>
{
public Names ()
{
Name aName = new Name("FirstName " + (this.Count +1).ToString(),
"LastName " + (this.Count + 1).ToString());
this.Add(aName);
}
}
Here my Name class :
class Name : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private string _firstName;
private string _lastName;
public Name(string fName, string lName)
{
_firstName = fName;
_lastName = lName;
}
public string FirstName
{
get
{
return _firstName;
}
set
{
_firstName = value;
PropertyChanged(this, new PropertyChangedEventArgs("FirstName"));
}
}
public string LastName
{
get
{
return _lastName;
}
set
{
_lastName = value;
PropertyChanged(this, new PropertyChangedEventArgs("LastName"));
}
}
}
The application cannot modify the content of this collection. See the Example section for an example of how to use this attached property.
The WPF data binding model enables you to associate ValidationRules with your Binding object. Validation occurs during binding target-to-binding source value transfer before the converter is called. The following describes the validation process:
1.When a value is being transferred from the target property to the source property, the data binding engine first removes any ValidationError that may have been added to the Validation.Errors attached property of the bound element. It then checks if there are any custom ValidationRules defined for that Binding, in which case it calls the Validate method on each of the ValidationRules until one of them runs into an error or until all of them pass.
2.Once there is a custom rule that does not pass, the binding engine creates a ValidationError object and adds it to the Validation.Errors collection of the bound element. When Validation.Errors is not empty, the Validation.HasError attached property of the element is set to true. Also, if the NotifyOnValidationError property of the Binding is set to true, then the binding engine raises the Validation.Error attached event on the element.
3.If all of the rules pass, the binding engine then calls the converter, if one exists.
4.If the converter passes, the binding engine calls the setter of the source property.
5.If the binding has an ExceptionValidationRule associated with it and an exception is thrown during step
4, the binding engine checks to see if there is a UpdateSourceExceptionFilter. You have the option to use the UpdateSourceExceptionFilter callback to provide a custom handler for handling exceptions. If an UpdateSourceExceptionFilter is not specified on the Binding, the binding engine creates a ValidationError with the exception and adds it to the Validation.Errors collection of the bound element.
Also note that a valid value transfer in either direction (target-to-source or source-to-target) clears the Validation.Errors attached property.
For information about the behavior of this property in MultiBinding scenarios, see ValidationError.
From your comment i would conclude that the ValidationRule does not return an error, hence the error event is not fired. Try stepping through the Validation-method with the debugger.
Also, validation is only performed upon a source-update, in TextBoxes that normally happens on LostFocus.
Edit: MyNames is a collection, it not have the properties you try to bind to, there should be binding errors in the Output window.
If you want to bind to the first element you need to change the path to something like [0].LastName for the last-name-binding.
Does your Names class implement INotifyPropertyChanged?
In the code-behind file of the file, set your datacontext to this. Expose your Names object as a property there and see if that works. I'm not comfortable with binding to the static resource in the window.
I have a datagrid whose itemsSource is bound to a multiconverter which uses a converter.
<toolkit:DataGrid AutoGenerateColumns="False">
<toolkit:DataGrid.ItemsSource>
<MultiBinding Converter="{StaticResource ProfileConverter}">
<Binding ElementName="ComboBoxProfiles" Path="SelectedValue" />
<Binding ElementName="DatePickerTargetDate" Path="SelectedDate" />
</MultiBinding>
</toolkit:DataGrid.ItemsSource>
This is good because the itemsSource of the grid is updated whenever the combobox or datepicker changes value.
The problem I now have is that in my ViewModel I want to be able to access the ItemSource of my datagrid and either remove items for the list or add new ones.
How do I get access to the itemssource when I have it set up like this?
Many thanks.
How about having three properties in the ViewModel:
public DateTime? SelectedDate
{
get{return _selectedDate;}
set
{
_selectedDate = value;
UpdateItemsSource();
OnPropertyChanged("SelectedDate");
}
}
public object SelectedComboBoxValue
{
get{return _selectedComboBoxValue;}
set
{
_selectedComboBoxValue= value;
UpdateItemsSource();
OnPropertyChanged("SelectedComboBoxValue");
}
}
private void UpdateItemsSource()
{
_itemsSource = //Some fancy expression based on the two fields.
OnPropertyChanged("ItemsSource");
}
public IEnumerable ItemsSource
{
get{return _itemsSource;}
}
Then bind the datepicker, combobox and datagrid to the respective values.
Hope this helps.