简体   繁体   中英

How can I update FontFamily and Foreground of a data bound ListBox using INotifyPropertyChanged

Scenario

I have Two Classes:

  • Fruit has two data members FruitName and FruitColor
  • Presentation also has two members ForeColor and FontName

A ListBox's ItemsSource property is set to List<Fruit> . And the fruits are listed.

However, I want to change FontFamily and Foreground of the TextBlock control, which should reflect immediately as I set the Presentation instance to the ListBox.

Problem

The problem is that

  • When I am calling the btnChangeColor_Click() , I receive an exception " Object reference not set to an instance of an object ". In the INotifyPropertyChanged() method. Exactly here...

    await dispatcher.RunAsync(CoreDispatcherPriority.Normal, () => { PropertyChanged(this, new PropertyChangedEventArgs(propertyName)); });

I want to set ListBox with some Fruits object showing FruitName and FruitColor. I also want to change font names and the foreground of the TextBlock showing the FruitName and the FruitColor, so that changing the color and font name should reflect immediately

Code

TestingRealTimeUIUpdate.xaml

<Page
    x:Class="dataStorage_And_AppSettings.TestingRealTimeUIUpdate"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="using:dataStorage_And_AppSettings"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d">

    <Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
        <StackPanel>
            <ListBox x:Name="lstFruits" Height="400" Background="Aqua">
            <ListBox.ItemTemplate>
                <DataTemplate>
                    <StackPanel>
                            <TextBlock x:Name="lblFruitName" Text="{Binding Fruits.FruitName}" Foreground="{Binding Presentations.ForeColor}" FontFamily="{Binding Presentations.FontName}" />
                            <TextBlock x:Name="lblFruitColor" Text="{Binding Fruits.FruitColor}" Foreground="{Binding Presentations.ForeColor}" FontFamily="{Binding Presentations.FontName}" />
                    </StackPanel>
                </DataTemplate>
            </ListBox.ItemTemplate>
        </ListBox>
            <ListBox x:Name="lstColors" Height="175" Background="Goldenrod" >
                <ListBox.ItemTemplate>
                    <DataTemplate>
                        <StackPanel>
                            <TextBlock x:Name="lblFruitName" Text="FontColors And Font Name" Foreground="{Binding Presentation.ForeColor}" FontFamily="{Binding Presentation.FontName}" />
                        </StackPanel>
                    </DataTemplate>
                </ListBox.ItemTemplate>
            </ListBox>
            <Button x:Name="btnReloadFruits" Content="ReloadBasket" Click="btnReload_Click" />
            <Button x:Name="btnChangeColor" Content="ChangeColor" Click="btnChangeColor_Click" />
        </StackPanel>
    </Grid>
</Page>

The TestingRealTimeUIUpdate.CS

using System;
using System.Collections.Generic;
using System.Linq;
using Windows.UI;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Media;
// The Blank Page item template is documented at http://go.microsoft.com/fwlink/?LinkId=234238

namespace dataStorage_And_AppSettings
{
    public sealed partial class TestingRealTimeUIUpdate : Page
    {
        private Comp FruitBasket;
        private List<Presentation> PresentationForFruitBasket = new List<Presentation>
        {
            new Presentation { FontName = "Arial", ForeColor = new SolidColorBrush(Colors.Green) },
            new Presentation { FontName = "Verdana", ForeColor = new SolidColorBrush(Colors.Yellow) },
            new Presentation { FontName = "Times New roman", ForeColor = new SolidColorBrush(Colors.Brown) },
            new Presentation { FontName = "Tahoma", ForeColor = new SolidColorBrush(Colors.Red) },
        };

        private List<Fruit> FruitForFruitBasket = new List<Fruit>
        {
            new Fruit { FruitName= "Mango", FruitColor="Yellow" },
            new Fruit {FruitName = "Banana", FruitColor= "Yellow" },
            new Fruit { FruitName="Grapes", FruitColor="Green"},
            new Fruit {FruitName="Tomato", FruitColor="Red" }
        };

        public TestingRealTimeUIUpdate()
        {
            this.InitializeComponent();
        }

        private void btnReload_Click(object sender, RoutedEventArgs e)
        {
            List<Comp> lstFruitBasket = new List<Comp>();
            foreach( var item in FruitForFruitBasket)
            {
                FruitBasket = new Comp();
                FruitBasket.Fruits = item;
                FruitBasket.Presentations = PresentationForFruitBasket.ElementAt(2);
                lstFruitBasket.Add(FruitBasket);
            }   
            lstFruits.ItemsSource = lstFruitBasket;
        }

        private void btnChangeColor_Click(object sender, RoutedEventArgs e)
        {
            Random rnd = new Random();
            FruitBasket.Presentations = PresentationForFruitBasket.ElementAt(rnd.Next(0, 3));
        }
    }

    public class Comp : BindableBase
    {
        Fruit fruits = new Fruit();
        Presentation presentations = new Presentation();

        public Fruit Fruits
        {
            get { return fruits; }
            set { SetProperty(ref fruits, value); }
        }

        public Presentation Presentations
        {
            get { return presentations; }
            set { SetProperty(ref presentations, value); }
        }
    }

    public class Fruit:BindableBase
    {
        private string fruitname;
        private string fruitcolor;
        public string FruitName
        {
            get { return fruitname; }
            set { SetProperty(ref fruitname, value); }
        }
        public string FruitColor
        {
            get { return fruitcolor; }
            set { SetProperty(ref fruitcolor, value); }
        }
    }

    public class Presentation : BindableBase
    {
        private SolidColorBrush forecolor;
        private string fontname;
        public SolidColorBrush ForeColor
        {
            get { return forecolor; }
            set { SetProperty(ref forecolor, value); }
        }

        public string FontName
        {
            get { return fontname; }
            set { SetProperty(ref fontname, value); }
        }
    }
}

The BindableBase.cs

using System;
using System.ComponentModel;
using System.Runtime.CompilerServices;
using Windows.UI.Core;

namespace IQ.Main.ViewModels
{
    public abstract class BindableBase : INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged;
        private CoreDispatcher dispatcher;
        protected bool SetProperty<T>(ref T storage, T value, [CallerMemberName] string propertyName = "")
        {
            if (Object.Equals(storage, value))
            {
                return false;
            }
            storage = value;
            NotifyPropertyChanged(propertyName);
            return true;
        }

        internal virtual async void NotifyPropertyChanged(string propertyName)
        {
            if (PropertyChanged != null)
            {
                try
                {
                    await dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
                    {
                        PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
                    });
                }
                catch (Exception ex) { Debug.WriteLine(ex.Message); }
            }
        }
    }
}

You can do something like this:

<ListBox Foreground="{x:Bind Foreground}" />

This works if the Foreground property is in your code-behind.

You can do something like this:

<ListBox Foreground="{Binding Foreground}" />

This works if the Foreground property is in your view-model.

You can also do this:

<ListBox>
  <ListBox.ItemTemplate>
    <DataTemplate>
      <TextBlock Text="{Binding Text}" Foreground="{Binding Foreground}" />
    </DataTemplate>
  </ListBox.ItemTemplate>
</ListBox>

This works if the Foreground property is in your model

Remember, you can always create a composite object. Like this:

class MyObject
{
    public FruitObject Fruit { get; set; }
    public PresentationObject Presentation { get; set; }
}

This will let you pass in multiple objects to any ItemControl .

Make sense? Best of luck.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM