c# - example - protected virtual void onpropertychanged
refrescante convertidor de valor en INotifyPropertyChanged (2)
Sé que hay algunos temas similares aquí, pero no pude obtener ninguna respuesta de ellos. Tengo que actualizar el fondo de una cuadrícula para una imagen o un color en mi aplicación Windows Phone 7. Lo hago utilizando mi conversor de valores, funciona bien, pero tendría que volver a cargar la colección para que actualice el color o la imagen.
<Grid Background="{Binding Converter={StaticResource ImageConverter}}" Width="125" Height="125" Margin="6">
El convertidor recibe el objeto y luego obtiene el color y la imagen del mismo, aquí está el convertidor
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;
}
}
Necesito actualizar / actualizar de alguna manera la etiqueta de la grilla o el convertidor de valor para que pueda mostrar los últimos cambios.
EDITAR
AÑADIDO ALGO MÁS CÓDIGO
El modelo :
[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:
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
}
Página de aplicación
<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>
Código de página de aplicación detrás
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}}"
sugiere que se vincule directamente al elemento People
(que es su problema al actualizar).
Entonces, deberías reorganizar un poco. En su lugar, haz que sea a property
de algún otro contexto de datos ''superior''.
Cómo reorganizar las cosas:
1) su ''modelo'' (entidad de db) debe ser diferente de su modelo de vista . Para evitar entrar en detalles, resuelve muchos problemas, por ejemplo, como si estuvieras teniendo. Los usuarios getters / setters normalmente no son anulados de esa manera (EF a menudo usa la reflexión para tratar con entidades, etc.).
Por lo tanto, haga que PeopleVM (para personas individuales o PersonViewModel
): copie elementos allí y haga que INotify esté allí; deje People como una entidad pura / poco con / get / set automático.
2) Lo mismo para PeopleViewModel
: está demasiado ligado al Db (estas son también pautas de diseño).
No debe reutilizar el DbContext , no lo guarde, es un objeto "único" (y almacenado en el caché), así que use using()
para tratar y cargar / actualizar bajo demanda.
3) Reemplace personas en su VM principal con PersonViewModel . Cuando cargue desde db, suba primero a PersonVM, cuando está guardando al revés. Es un truco complicado con MVVM, a menudo necesita copiar / duplicar: puede usar alguna herramienta para automatizar o simplemente hacer copias de códigos o algo.
Your ObservableCollection<People> AllPeople
convierte en ObservableCollection<PersonViewModel> AllPeople
4) XAML: su enlace AllPeople, PeopleName es el mismo, pero eso apunta ahora a view-models (y Name to VM Name).
Pero debe unir su grid
a algo que no sea PersonViewModel
(personas mayores) , ya que es difícil "actualizar" cuando está dentro de la colección.
a) Cree una nueva propiedad única, como ImageAndTileColor
, y asegúrese de que actualice / notifique en / cuando cambie cualquiera de las dos propiedades.
b) La otra opción es usar MultiBinding
- y enlazar 2, 3 propiedades - una es el PersonViewModel
completo como usted y además de esas otras dos propiedades, por ejemplo ...
<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>
De esta forma, obligará a la encuadernación a actualizarse cuando se produzca cualquiera de los 3 cambios , y todavía tendrá toda su Gente allí (en realidad podría usar dos, ya que todo lo que necesita es Imagen y Color de mosaico).
5) Cambie su convertidor para que sea IMultiValue ... y para leer los valores múltiples enviados.
Eso es todo :)
VERSION CORTA:
Esa era la proper way
y segura de trabajar (depende de cómo / cuándo se actualicen las propiedades de Persona, etc.), pero primero se podría probar la short version
, simplemente hacer la parte multi-binding
en el modelo People y esperar que trabajo. Si no tiene que hacer todo lo anterior.
Windows Phone 7:
Ya que no hay MultiBinding
...
- Utilice la solución temporal - debería ser bastante similar,
- O vaya con (a)
arriba - vincular la cuadrícula a {Binding ImageAndTileColor, Converter...}
. Haga una nueva propiedad (puede hacer lo mismo si lo desea en la entidad / modelo; simplemente márquela como [NotMapped()]
) que sería una ''compuesta''.
Lo tengo (Gracias a NSGaga). Configuré su publicación como respuesta, lo siguiente es lo que hice
Primero necesitaba convertir el convertidor para recibir PeopleId en lugar del objeto en sí
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;
}
}
Luego solo tuve que agregar la llamada NotifyPropertyChanged("PeopleId")
cada vez que actualizo Image o TileColor como este
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");
}
}
}
Esto obliga al convertidor de valor a actualizar :)