开发者

When will the ValueConverter's Convert method be called in wpf

开发者 https://www.devze.com 2022-12-29 19:54 出处:网络
I have an ObservableCollection bound to a list box and a boolean property bound to a button. I then defined two converters, one that operates on the collection and the other operates on the boolean pr

I have an ObservableCollection bound to a list box and a boolean property bound to a button. I then defined two converters, one that operates on the collection and the other operates on the boolean property. Whenever I modify the boolean property, the converter's Convert method is called, where as the same is not called if I modify the observable collection. What am I missing??

Snippets for your reference,

xaml snipet,

<Window.Resources>
    <local:WrapPanelWidthConverter x:Key="WrapPanelWidthConverter" />
    <local:StateToColorConverter x:Key="StateToColorConverter" />
</Window.Resources>
<StackPanel>
    <ListBox x:Name="NamesListBox" ItemsSource="{Binding Path=Names}">
        <ListBox.ItemsPanel>
            <ItemsPanelTemplate>
                <WrapPanel x:Name="ItemWrapPanel" Width="500" Background="Gray">
                    <WrapPanel.RenderTransform>
                        <TranslateTransform x:Name="WrapPanelTranslatation" X="0" />
                    </WrapPanel.RenderTransform>
                    <WrapPanel.Triggers>
                        <EventTrigger RoutedEvent="WrapPanel.Loaded">
                            <BeginStoryboard>
                                <Storyboard>
                                    <DoubleAnimation Storyboard.TargetName="WrapPanelTranslatation" Storyboard.TargetProp开发者_StackOverflow中文版erty="X" To="{Binding Path=Names,Converter={StaticResource WrapPanelWidthConverter}}" From="525"  Duration="0:0:2" RepeatBehavior="100" />
                                </Storyboard>
                            </BeginStoryboard>
                        </EventTrigger>
                    </WrapPanel.Triggers>
                </WrapPanel>
            </ItemsPanelTemplate>
        </ListBox.ItemsPanel>
        <ListBox.ItemTemplate>
            <DataTemplate>
                <Grid>
                    <Label Content="{Binding}" Width="50" Background="LightGray" />
                </Grid>
            </DataTemplate>
        </ListBox.ItemTemplate>
    </ListBox>
    <Button Content="{Binding Path=State}" Background="{Binding Path=State, Converter={StaticResource StateToColorConverter}}" Width="100" Height="100" Click="Button_Click" />
</StackPanel>   

code behind snippet

public class WrapPanelWidthConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        ObservableCollection<string> aNames = value as ObservableCollection<string>;
        return -(aNames.Count * 50);
    }

    public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        throw new NotImplementedException();
    }
}


public class StateToColorConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        bool aState = (bool)value;
        if (aState)
            return Brushes.Green;
        else
            return Brushes.Red;
    }

    public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        throw new NotImplementedException();
    }
}   


A multibinding converter can be used to overcome this issue. You can then bind to the Collection.Count property and the collection at the same time. The count will trigger the binding to get re-evaluated and then you use the second binding to actually transform the values as required

<TextBlock IsHitTestVisible="false"
    Margin="5,0"
    TextTrimming="CharacterEllipsis"
    VerticalAlignment="Center"
    DockPanel.Dock="Left" >
    <TextBlock.Text>
        <MultiBinding Converter="{Resources:ListToStringConverter}">
            <Binding Path="List.Count" />
            <Binding Path="List" />
        </MultiBinding>
    </TextBlock.Text>
</TextBlock>


I think the converter in a Binding is always called if the Binding source has been updated and notifies about that update (as a DependencyProperty or using INotifyPropertyChanged). However, an ObservableCollection does not raise the PropertyChanged event if an item has been added or removed, but it raises the CollectionChanged event. It does not raise any event at all if an item in the collection is changed. Even if the item itself raises PropertyChanged, this will not update the Binding on the collection since the Binding source is not the item, but the collection.

I fear your approach will not work this way. You could bind directly to ObservableCollection.Count and add an appropriate math converter to it to perform the inversion and multiplication, but the Count property does not perform change notification, so this no option. I think you will have to provide another property in your ViewModel or code-behind which handles these cases...


A converter is called when the binding happens or the property changes. So your converter is called for your boolean whenever the value of the boolean changes. Your collection is set once and that is when the binding occurs and the converter is used. When the internals of the collection change (the collection is added to or removed from) the property does not change (i.e. you are not binding a new collection) so your converter does not fire again.

Use a view model and wrap your collection and add another property such as count that implements change notification. You can use this wrapper class from here which will wrap your collection and it will be easy to add a property there.

0

精彩评论

暂无评论...
验证码 换一张
取 消