.NET: Difficulty with Events
Perhaps I don't understand events fully.
I'm building a Windows Phone 7 app in Silverlight.
I have a UserControl
that wraps a ListBox
, called EditableListBox
. The ListBox
has a data template. The items in the list box are wrapped by EditableListItem
objects.
The data template is as follows:
<DataTemplate>
<Grid ManipulationCompleted="Grid_ManipulationCompleted">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Image Source="{Binding Path=IconSource}"
Grid.Column="0"
Width="96"
Height="96"
VerticalAlignment="Center"
Visibility="{Binding Path=Editing, Converter={StaticResource visibilityConverter}}"
/>
<TextBlock Text="{Binding Path=Name}" Grid.Column="1" />
</Grid>
</DataTemplate>
I'm binding the Visibility
to a property of each EditableListItem
, so I need to implement INotifyPropertyChanged
so updates to the backing items are reflected in the UI. (Right? Or is there a simpler way to do it?)
EditableListItem
:
public class EditableListItem : INotifyPropertyChanged
{
private EditableListBox _parentListBox;
public event PropertyChangedEventHandler PropertyChanged;
public bool Editing
{
get
{
return _parentListBox.Editing;
}
}
public EditableListItem(Section section, EditableLis开发者_Python百科tBox parentListBox)
{
_parentListBox = parentListBox;
// after this line, _parentListBox.PropertyChanged is still null.
// why is that?
_parentListBox.PropertyChanged += PropertyChanged;
_parentListBox.PropertyChanged += new PropertyChangedEventHandler(_parentListBox_PropertyChanged);
}
EditableListBox
:
public partial class EditableListBox : UserControl, INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
// NotifyPropertyChanged will raise the PropertyChanged event,
// passing the source property that is being updated.
public void NotifyPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
public void SetSectionsSource(ObservableCollection<Section> sectionsSource)
{
sectionsSource.CollectionChanged += new NotifyCollectionChangedEventHandler(sectionsSource_CollectionChanged);
ContentListBox.ItemsSource = sectionsSource.Select(section => new EditableListItem(section, this) { Enabled = true });
//ContentListBox.ItemsSource.Add(new EditableListItem(new Section("Section", 3)) { Enabled = true });
}
// ...
private bool _editing;
public bool Editing
{
get
{
return _editing;
}
set
{
_editing = value;
NotifyPropertyChanged("Editing");
}
}
}
The Editing
property is stored in EditableListBox
- EditableListItem
just forwards it. I wanted to attached EditableListItem.PropertyChanged
to EditableListBox.PropertyChanged
directly, but the following didn't work:
// after this line, _parentListBox.PropertyChanged is still null.
// why is that?
_parentListBox.PropertyChanged += PropertyChanged;
The following did work:
_parentListBox.PropertyChanged += new PropertyChangedEventHandler(_parentListBox_PropertyChanged);
Why is this? Is the first attempt totally invalid (if so, why does the compiler allow it?)?
To begin with, you don't wire up the PropertyChanged
to implement it. The idea is that WPF uses that event and it wires it up. The only thing you do is trigger the event when applicable.
And that's a part of the issue here. You have the Editing
property, but it is not being fired. I do understand that you have wired the PropertyChanged
of the parent listbox to get the event to fire, but that is not going to work.
If I get the idea right, what you want to accomplish is when the Editing
property of the listbox gets changed, you want the PropertyChanged
of the list item to be forced.
One of the things of PropertyChanged
is that the sender has to be the object where the PropertyChanged
is located. This means that you should implement it like this:
public partial class EditableListBox : UserControl, INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
// You really should make this protected. You do not want the outside world
// to be able to fire PropertyChanged events for your class.
protected void NotifyPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
private bool _editing;
public bool Editing
{
get
{
return _editing;
}
set
{
_editing = value;
NotifyPropertyChanged("Editing");
}
}
}
public class EditableListItem : INotifyPropertyChanged
{
private EditableListBox _parentListBox;
public EditableListItem(EditableListBox parentListBox)
{
_parentListBox = parentListBox;
_parentListBox.PropertyChanged += new PropertyChangedEventHandler(_parentListBox_PropertyChanged);
}
void _parentListBox_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
// Forward the event.
if (e.PropertyName == "Editing")
NotifyPropertyChanged("Editing");
}
public event PropertyChangedEventHandler PropertyChanged;
// You really should make this protected. You do not want the outside world
// to be able to fire PropertyChanged events for your class.
protected void NotifyPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
public bool Editing
{
get
{
return _parentListBox.Editing;
}
}
}
I don't know how you get the reference to the editable listbox, but lets say you get it via the constructor. When you get the reference, you attach the the PropertyChanged
event handler of the listbox. Because, when the Editing
property of that object changes, actually, your Editing
property changes too. This is how you simulate that.
One last thing: the reason why the PropertyChanged
is still null
after the += PropertyChanged
is because the PropertyChanged
of the object itself is null. You cannot wire the events in this way. The second way is the correct way of wiring up the events, and the above example shows what you do with this.
精彩评论