WP7: change the background of a border with conver

2019-08-26 18:57发布

问题:

I'm going crazy with converters. I know that I must use it to change the "exit value" of my values, when needed, but I don't know how to use right for my case.

I have my simple MVVM (3 fields only) and my main window with a list of my items. The first item is calculated depending on a function, and can show YES or NOT, the other values are binded directly.

This is working well, but I need to change the background and foreground colors depending on the YES or NOT value I have in the first calculated field. For example:

YES (must be blue) - ITEM 1
NO (must be grey)  - ITEM 2
YES (must be blue) - ITEM 3

While the internal values in my database are (in this case the calc is modulus):

2 - ITEM 1
3 - ITEM 2
4 - ITEM 3

My ListBox code is this:

<phone:PhoneApplicationPage 
x:Class="Pasti.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:phone="clr-namespace:Microsoft.Phone.Controls;assembly=Microsoft.Phone"
xmlns:shell="clr-namespace:Microsoft.Phone.Shell;assembly=Microsoft.Phone"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d" d:DesignWidth="480" d:DesignHeight="768"
FontFamily="{StaticResource PhoneFontFamilyNormal}"
FontSize="{StaticResource PhoneFontSizeNormal}"
Foreground="{StaticResource PhoneForegroundBrush}"
SupportedOrientations="Portrait" Orientation="Portrait"
shell:SystemTray.IsVisible="True">

<Grid x:Name="LayoutRoot" Background="Transparent">
    <Grid.RowDefinitions>
        <RowDefinition Height="Auto"/>
        <RowDefinition Height="*"/>
    </Grid.RowDefinitions>

    <StackPanel x:Name="TitlePanel" Grid.Row="0" Margin="12,17,0,28">
        <TextBlock Text="My App" Style="{StaticResource PhoneTextNormalStyle}" />
        <TextBlock Text="My List" Style="{StaticResource PhoneTextTitle1Style}" />
    </StackPanel>

<ListBox x:Name="lstPills" Grid.Row="1" ItemsSource="{Binding AllItems}">
    <ListBox.ItemTemplate>
        <DataTemplate>
            <Grid HorizontalAlignment="Stretch" Width="440">
                <Grid.ColumnDefinitions>
                    <ColumnDefinition Width="90" />
                    <ColumnDefinition Width="*" />
                </Grid.ColumnDefinitions>
                <Border Background="HERE MUST GO THE CONVERTER, I SUPOSE">
                    <TextBlock Text="{Binding IsPair, Mode=TwoWay}"/>
                </Border>
                <TextBlock
                    Text="{Binding Name}"
                    FontSize="{StaticResource PhoneFontSizeLarge}"
                    Grid.Column="1"
                    VerticalAlignment="Center"/>
            </Grid>
        </DataTemplate>
    </ListBox.ItemTemplate>
</ListBox>
</Grid>
</phone:PhoneApplicationPage>

And the CS code is this for this page:

public partial class MainPage : PhoneApplicationPage
{
    // Constructor
    public MainPage()
    {
        InitializeComponent();

        // Set the page DataContext property to the ViewModel.
        this.DataContext = App.ViewModel;
    }
}

For the calculated field, I added this to the Model (_myNumber holds the value I must check):

    // Define a custom field based on some database values
    // Get is calculated, while set will force it to refresh by Notifying
    public string IsPair
    {
        get
        {
            return _myNumber % 2 == 0 ? "YES" : "NO";
        }
        set
        {
            NotifyPropertyChanged("IsPair");
        }
    }

NOTE: Because I don't know other way to force the list to refresh, I put the set property to only notify and the TwoWay Mode, and I just do a IsPair = "" when I want it to recalculate. If there are other way to do it, will be welcome.

So, with this info, how can I made a Converter that, based on my IsPair value, set the Background property of the Border to Blue or Grey? I saw a lot of Converter examples, but still don't get the point to do exactly this.

I suppose I must put something like this in the MainPage.cs, under the MainPage Class:

// Converter for the YES-NO column on the list
public class IsPairConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        if (MY_CALCULATED_VALUE == "YES")
            return "Blue";

        return "Grey";
    }

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

But how to get the MY_CALCULATED_VALUE, and how to set the converter in the Background value of the Border?

回答1:

So close!

First, bind the background to IsPair and use the converter:

<Border Background="{Binding IsPair, Converter={StaticResource IsPairConverter}}">
    <TextBlock Text="{Binding IsPair, Mode=TwoWay}"/>
</Border>

In your converter, create a brush depending on the value:

public class IsPairConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        // You might want to add additional checks for type safety
        var calculatedValue = (string)value; 

        var color = calculatedValue == "YES" ? Colors.Blue : Colors.Gray;

        return new SolidColorBrush { Color = color };
    }

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

And you're done.


If you want the value to be computed only one time, instead of every time IsPair is called, you can do the computation in the setter of MyNumber and assign it to IsPair:

private int myNumber;

public string IsPair { get; protected set; }

protected int MyNumber
{
    get
    {
        return this.myNumber;
    }

    set
    {
        this.myNumber = value;
        this.IsPair = value % 2 == 0 ? "YES" : "NO";
        this.NotifyPropertyChanged("IsPair");
    }
}