简体   繁体   English

在INotifyPropertyChanged上刷新值转换器

[英]refreshing Value Converter on INotifyPropertyChanged

I know there are some similar topics here but I couldn't get any answer from them. 我知道这里有一些类似的主题,但我无法得到任何答案。 I have to update background of a grid to either an image or a colour in my Windows Phone 7 app. 我必须在Windows Phone 7应用程序中将网格背景更新为图像或颜色。 I do this using my value converter , it works fine but I'd have to reload the collection so it updates the colour or image. 我使用我的值转换器,它工作正常,但我必须重新加载集合,以便更新颜色或图像。

<Grid Background="{Binding Converter={StaticResource ImageConverter}}" Width="125" Height="125" Margin="6">

The converter receives the object then gets the color and image from it, here is the converter 转换器接收对象然后从中获取颜色和图像,这里是转换器

public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
        {

            People myC = value as People;

            string myImage = myC.Image;
            object result = myC.TileColor;

            if (myImage != null)
            {

                BitmapImage bi = new BitmapImage();
                bi.CreateOptions = BitmapCreateOptions.BackgroundCreation;
                ImageBrush imageBrush = new ImageBrush();

                using (IsolatedStorageFile myIsolatedStorage = IsolatedStorageFile.GetUserStoreForApplication())
                {
                    if (myIsolatedStorage.FileExists(myImage))
                    {

                        using (
                            IsolatedStorageFileStream fileStream = myIsolatedStorage.OpenFile(myImage, FileMode.Open,
                                                                                              FileAccess.Read))
                        {
                            bi.SetSource(fileStream);
                            imageBrush.ImageSource = bi;
                        }
                    }
                    else
                    {
                        return result;
                    }
                }

                return imageBrush;
            }
            else
            {
                return result;
            }

    }

I need to somehow update/refresh the grid tag or the value converter so that it can show the latest changes ! 我需要以某种方式更新/刷新网格标签或值转换器,以便它可以显示最新的更改!

EDIT 编辑

ADDED SOME MORE CODE 添加了更多代码

The model : 该模型 :

  [Table]
    public class People : INotifyPropertyChanged, INotifyPropertyChanging
    {


        private int _peopleId;

        [Column(IsPrimaryKey = true, IsDbGenerated = true, DbType = "INT NOT NULL Identity", CanBeNull = false, AutoSync = AutoSync.OnInsert)]
        public int PeopleId
        {
            get { return _peopleId; }
            set
            {
                if (_peopleId != value)
                {
                    NotifyPropertyChanging("PeopleId");
                    _peopleId = value;
                    NotifyPropertyChanged("PeopleId");
                }
            }
        }

        private string _peopleName;

        [Column]
        public string PeopleName
        {
            get { return _peopleName; }
            set
            {
                if (_peopleName != value)
                {
                    NotifyPropertyChanging("PeopleName");
                    _peopleName = value;
                    NotifyPropertyChanged("PeopleName");
                }
            }
        }




        private string _tileColor;

        [Column]
        public string TileColor
        {
            get { return _tileColor; }
            set
            {
                if (_tileColor != value)
                {
                    NotifyPropertyChanging("TileColor");
                    _tileColor = value;
                    NotifyPropertyChanged("TileColor");
                }
            }
        }



        private string _image;

        [Column]
        public string Image
        {
            get { return _image; }
            set
            {
                if (_image != value)
                {
                    NotifyPropertyChanging("Image");
                    _image = value;
                    NotifyPropertyChanged("Image");
                }
            }
        }


        [Column]
        internal int _groupId;

        private EntityRef<Groups> _group;

        [Association(Storage = "_group", ThisKey = "_groupId", OtherKey = "Id", IsForeignKey = true)]
        public Groups Group
        {
            get { return _group.Entity; }
            set
            {
                NotifyPropertyChanging("Group");
                _group.Entity = value;

                if (value != null)
                {
                    _groupId = value.Id;
                }

                NotifyPropertyChanging("Group");
            }
        }


        [Column(IsVersion = true)]
        private Binary _version;

        #region INotifyPropertyChanged Members

        public event PropertyChangedEventHandler PropertyChanged;

        private void NotifyPropertyChanged(string propertyName)
        {
            if (PropertyChanged != null)
            {
                PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
            }
        }

        #endregion

        #region INotifyPropertyChanging Members

        public event PropertyChangingEventHandler PropertyChanging;

        private void NotifyPropertyChanging(string propertyName)
        {
            if (PropertyChanging != null)
            {
                PropertyChanging(this, new PropertyChangingEventArgs(propertyName));
            }
        }

        #endregion
    }

ViewModel : ViewModel:

public class PeopleViewModel : INotifyPropertyChanged
{

    private PeopleDataContext PeopleDB;

    // Class constructor, create the data context object.
    public PeopleViewModel(string PeopleDBConnectionString)
    {
        PeopleDB = new PeopleDataContext(PeopleDBConnectionString);
    }


    private ObservableCollection<People> _allPeople;

    public ObservableCollection<People> AllPeople
    {
        get { return _allPeople; }
        set
        {
            _allPeople = value;
            NotifyPropertyChanged("AllPeople");
        }
    }

    public ObservableCollection<People> LoadPeople(int gid)
    {
        var PeopleInDB = from People in PeopleDB.People
                           where People._groupId == gid
                           select People;


        AllPeople = new ObservableCollection<People>(PeopleInDB);

        return AllPeople;
    }


    public void updatePeople(int cid, string cname, string image, string tilecol)
    {
        People getc = PeopleDB.People.Single(c => c.PeopleId == cid);
        getc.PeopleName = cname;
        getc.Image = image;
        getc.TileColor = tilecol;

        PeopleDB.SubmitChanges();

    }

    #region INotifyPropertyChanged Members

    public event PropertyChangedEventHandler PropertyChanged;

    public void NotifyPropertyChanged(string propertyName)
    {
                if (PropertyChanged != null)
                {
                    PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
                }
    }

    #endregion
}

Application Page 申请页面

    <Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">

        <ListBox Margin="0,8,0,0"  x:Name="Peoplelist" HorizontalAlignment="Center"  BorderThickness="4" ItemsSource="{Binding AllPeople}">
            <ListBox.ItemTemplate>
                <DataTemplate>
                    <Grid Background="{Binding Converter={StaticResource ImageConverter}}" Width="125" Height="125" Margin="6">
                        <TextBlock Name="name" Text="{Binding PeopleName}" VerticalAlignment="Center" HorizontalAlignment="Center" TextAlignment="Center" TextWrapping="Wrap"/>
                    </Grid>
                </DataTemplate>
            </ListBox.ItemTemplate>
            <ListBox.ItemsPanel>
                <ItemsPanelTemplate>
                    <toolkit:WrapPanel/>
                </ItemsPanelTemplate>
            </ListBox.ItemsPanel>
        </ListBox>

    </Grid>

Application Page code behind 应用程序页面代码

public partial class PeopleList : PhoneApplicationPage
{

    private int gid;
    private bool firstRun;

    public PeopleList()
    {
        InitializeComponent();
        firstRun = true;
        this.DataContext = App.ViewModel;
    }

    protected override void OnNavigatedTo(NavigationEventArgs e)
    {

        gid = int.Parse(NavigationContext.QueryString["Id"]);

        if (firstRun)
        {
            App.ViewModel.LoadPeople(gid);
            firstRun = false;
        }


    }

 }

Background="{Binding Converter={StaticResource ImageConverter}}" suggests you bind directly to People item (which is your problem when refreshing). Background="{Binding Converter={StaticResource ImageConverter}}"建议您直接绑定到People (这是刷新时的问题)。

So, you should rearrange a bit. 所以,你应该重新安排一下。 Make that a property of some other 'higher' data context instead. 将其a property某些其他“更高”数据上下文a property


How to rearrange things: 如何重新安排事情:

1) your 'model' (entity from db) should be different than your view-model . 1) 您的“模型”(来自db的实体)应该与您的视图模型不同 To avoid going into details, it solves lot of problems - eg like you're having. 为了避免详细说明,它解决了许多问题 - 例如你所拥有的问题。 The People getters/setters are not normally overriden in that way (EF often uses reflection to deal w/ entities etc.). People getters / setter通常不以这种方式覆盖(EF经常使用反射来处理w /实体等)。
So, make PeopleVM (for single People or PersonViewModel ) - copy things in there - and make INotify in there - leave People just a pure entity/poco w/ automatic get/set. 因此,让PeopleVM(对于单人或PersonViewModel ) - 在那里复制东西 - 并在那里制作INotify - 留下人们只是一个纯粹的实体/ poco w /自动获取/设置。

2) Same for the PeopleViewModel - it's too tied-up to the Db (these are also design guidelines). 2) PeopleViewModel也是PeopleViewModel - 它与Db过于紧密(这些也是设计指南)。
You shouldn't reuse the DbContext - don't save it - it's a 'one off' object (and cached inside) - so use using() to deal with and load/update on demand. 你不应该重用DbContext - 不要保存它 - 它是一个'一次性'对象(并缓存在里面) - 所以使用using()来按需处理和加载/更新。

3) Replace People in your main VM with PersonViewModel . 3) 用PersonViewModel替换主VM中的People When you load from db, pump up into the PersonVM first - when you're saving the other way around. 当您从db加载时,首先将其泵入PersonVM - 当您以相反的方式保存时。 That's a tricky bit with MVVM, you often need to copy/duplicate - you could use some tool for that to automate or just make copy ctor-s or something. 对于MVVM来说这是一个棘手的问题,你经常需要复制/复制 - 你可以使用一些工具来自动化或只是复制ctor-s或其他东西。
Your ObservableCollection<People> AllPeople becomes ObservableCollection<PersonViewModel> AllPeople 你的ObservableCollection<People> AllPeople成为ObservableCollection<PersonViewModel> AllPeople

4) XAML - your binding AllPeople, PeopleName is the same - but that points now to view-models (and Name to VM Name). 4)XAML - 您的绑定AllPeople,PeopleName是相同的 - 但现在指向视图模型(和名称到VM名称)。
But you should bind your grid to something other than PersonViewModel (old People) - as that is hard to 'refresh' when inside the collection. 但是你应该将你的grid绑定到PersonViewModel (旧人)以外的东西 - 因为在集合内部很难“刷新”。
a) Make a new single property like ImageAndTileColor - and make sure it updates/notifies on / when any of the two properties change. a)创建一个新的单一属性,如ImageAndTileColor - 并确保它在/当两个属性中的任何一个更改时更新/通知。
b) The other option is to use MultiBinding - and bind 2, 3 properties - one being the whole PersonViewModel like you have and plus those other two properties - eg.. b)另一个选择是使用MultiBinding - 并绑定2,3个属性 - 一个是你拥有的整个PersonViewModel ,再加上其他两个属性 - 例如..

<Grid ...>
    <Grid.Background>
        <MultiBinding Converter="{StaticResource ImageConverter}" Mode="OneWay">
            <MultiBinding.Bindings>
                <Binding Path="Image" />
                <Binding Path="TileColor" />
                <Binding Path="" />
            </MultiBinding.Bindings>
        </MultiBinding>
    </Grid.Background>
    <TextBlock Name="name" Text="{Binding PeopleName}" ... />
</Grid>

That way you force the binding to refresh when any of the 3 changes - and you still have your full People in there (actually you could just use two as all you need is Image and TileColor). 这样你就可以在3次更改时强制绑定刷新 - 你仍然拥有完整的People(实际上你可以只使用两个,因为你需要的只有Image和TileColor)。

5) Change your Converter to be IMultiValue... and to read multiple values sent in. 5)将您的转换器更改为IMultiValue ...并读取发送的多个值。

That's all :) 就这样 :)

SHORT VERSION: 简洁版本:
That was the proper way and certain to work (it depends on how/when you update Person properties etc.) - but you could try the short version first - just do the multi-binding part on the People model - and hope it'd work. 这是proper way并确定工作(这取决于你如何/何时更新Person属性等) - 但你可以先尝试short version - 只需在People模型上做multi-binding部分 - 并希望它能够工作。 If it doesn't you have to do all the above. 如果不是,你必须做以上所有。

Windows Phone 7: Windows Phone 7:
Since there is no MultiBinding ... 由于没有MultiBinding ......
- Use the workaround - it should be quite similar, - 使用变通方法 - 它应该非常相似,
- Or go with the (a) above - bind grid to {Binding ImageAndTileColor, Converter...} . - 或者使用上面的(a) - 绑定网格到{Binding ImageAndTileColor, Converter...} Make new property (you can do the same if you wish in the entity/model - just mark it as [NotMapped()] ) which would be a 'composite' one. 创建新属性(如果您希望在实体/模型中也可以这样做 - 只需将其标记为[NotMapped()] ),这将是一个“复合” [NotMapped()]


http://www.thejoyofcode.com/MultiBinding_for_Silverlight_3.aspx http://www.thejoyofcode.com/MultiBinding_for_Silverlight_3.aspx

I got it (Thanks to NSGaga). 我明白了(感谢NSGaga)。 I set his post as the answer, the following is what I did 我把他的帖子作为答案,以下是我所做的

First I needed to make the converter to receive PeopleId instead of the object itself 首先,我需要使转换器接收PeopleId而不是对象本身

public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
        {

        int cid = (int)value;
        People myC = App.ViewModel.getPerson(cid);

            string myImage = myC.Image;
            object result = myC.TileColor;

            if (myImage != null)
            {

                BitmapImage bi = new BitmapImage();
                bi.CreateOptions = BitmapCreateOptions.BackgroundCreation;
                ImageBrush imageBrush = new ImageBrush();

                using (IsolatedStorageFile myIsolatedStorage = IsolatedStorageFile.GetUserStoreForApplication())
                {
                    if (myIsolatedStorage.FileExists(myImage))
                    {

                        using (
                            IsolatedStorageFileStream fileStream = myIsolatedStorage.OpenFile(myImage, FileMode.Open,
                                                                                              FileAccess.Read))
                        {
                            bi.SetSource(fileStream);
                            imageBrush.ImageSource = bi;
                        }
                    }
                    else
                    {
                        return result;
                    }
                }

                return imageBrush;
            }
            else
            {
                return result;
            }

    }

Then I just had to add call NotifyPropertyChanged("PeopleId") whenever I update Image or TileColor like this 然后,每当我像这样更新Image或TileColor时,我只需要添加调用NotifyPropertyChanged("PeopleId")

    private string _tileColor;

    [Column]
    public string TileColor
    {
        get { return _tileColor; }
        set
        {
            if (_tileColor != value)
            {
                NotifyPropertyChanging("TileColor");
                _tileColor = value;
                NotifyPropertyChanged("TileColor");
                NotifyPropertyChanged("PeopleId");
            }
        }
    }



    private string _image;

    [Column]
    public string Image
    {
        get { return _image; }
        set
        {
            if (_image != value)
            {
                NotifyPropertyChanging("Image");
                _image = value;
                NotifyPropertyChanged("Image");
                NotifyPropertyChanged("PeopleId");
            }
        }
    }

This forces the value converter to refresh :) 这会强制值转换器刷新:)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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