binding event handler to custom object - c#

How can I bind event handler to the custom object that i created?
here is my XAML
<ListBox x:Name="ListData">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Margin="0,0,0,30">
<Image VerticalAlignment="Top" HorizontalAlignment="Left" Source="{Binding Path=TileImage}" Width="175" Height="175" />
<TextBlock Margin="5" Width="200" TextWrapping="Wrap" VerticalAlignment="Top" HorizontalAlignment="Left" Text="{Binding Path=TileName}" FontSize="25"/>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
Code Behind
// Create collection
ImageTiles = new ObservableCollection<ImageTile>();
// Create each object in the collection
ImageTile RSS= new ImageTile("RSS", "/Images/Hard.jpg");
ImageTile test= new ImageTile("test", "/Images/Hard.jpg");
ImageTile Exam= new ImageTile("Exam", "/Images/Hard.jpg");
ImageTile Settings = new ImageTile("Settings", "/Images/Hard.jpg");
ImageTiles.Add(RSS);
ImageTiles.Add(test);
ImageTiles.Add(Exam);
ImageTiles.Add(Settings);
this.ListData.ItemsSource = ImageTiles;
I would like to bind the event handler along with each ImageTile. Any idea how to do so? =)

Based on your code structure I'll answer assuming you're not using MVVM or the like, however I'd definitely recommend that pattern for Silverlight development.
Nevertheless, your datasource binding would be something like:
<ListBox x:Name="ListData" ItemsSource="{Binding ImageTiles}">
</ListBox>
You could create a single generic click handler in your code behind and assign the Image's click event to that handler.
<ImageButton VerticalAlignment="Top" HorizontalAlignment="Left" Source="{Binding Path=TileImage}" Width="175" Height="175" Click="imageButton_Click" />
You could then have a method on your object responsible for redirecting to whatever necessary place for that particular image tile. Then in your code behind handler would resemble this:
private void imageButton_Click(object sender, RoutedEventArgs e)
{
var imageTile = ((ImageButton)sender).DataContext as ImageTile;
imageTile.RedirectSomewhere();
}

In your ViewModel, add a property to capture selected item (e.g.)
private ImageTile _selectedItem;
public ImageTile SelectedItem
{
get {return _selectedItem;}
set
{
if(value != _selectedItem)
{
_selectedItem = value;
RaisePropertyChanged("SelectedItem");
}
}
}
Then, in your XAML, bind the SelectedItem:
<ListBox ItemsSource="{Binding ImageTiles}" SelectedItem="{Binding SelectedItem, Mode=TwoWay}">
</ListBox>
Use MVVM Light (or some other way) to bind the SelectionChanged event to a command, or wire up an event handler for the SelectionChanged event. In your Command (or event handler), get SelectedItem, and look at the first property (you didn't tell us what it was called, so I don't know what to call it).
Remember to make sure that SelectedItem is not null before you do anything, and, when you're done handling the command, set SelectedIndex to -1, so that they can select the same item twice, and still get the functionality to execute.

Related

Dependencyproperty of type observablecollection throw exception

I'm making a user-control which is called "FileSelector" based on listbox. The list box is filled with an observablecollection "FileDisplay" which contains file names selected from a dialog.
<ListBox x:Name="FileListBox" Template="{DynamicResource BaseListBoxControlStyle}" Grid.RowSpan="5" Grid.Row="1" Margin="0" ItemContainerStyle="{DynamicResource BaseListBoxItemStyle}" ItemsSource="{Binding DataContext.FileDisplay, ElementName=F_Selector, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" ScrollViewer.HorizontalScrollBarVisibility="Disabled" HorizontalContentAlignment="Stretch">
<ListBox.ItemTemplate>
<DataTemplate>
<Grid>
<DockPanel>
<Button x:Name="ListDelete" Width="{Binding ActualHeight, ElementName=ListDelete}" Style="{DynamicResource BaseButtonStyle}" Margin="4,0,0,0" DockPanel.Dock="Right" Content="X" Click="FileDelete_Click"/>
<StackPanel HorizontalAlignment="Left" Orientation="Horizontal">
<ToggleButton x:Name="ListCheck" Width="{Binding ActualHeight, ElementName=ListCheck}" Style="{DynamicResource BaseToggleButtonStyle}" Margin="0,0,4,0" Checked="File_Checked" Unchecked="File_Unchecked" />
<TextBlock Text="{Binding ., Converter={StaticResource PathToFileName}}" TextTrimming="CharacterEllipsis"/>
</StackPanel>
</DockPanel>
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
When a toggle button contained in the item is checked, I want to add the content of the item to a dependencyproperty of type observablecollection.
private void File_Checked(object sender, RoutedEventArgs e)
{
ToggleButton btn = (ToggleButton)sender;
int index = FileListBox.Items.IndexOf(btn.DataContext);
FileChecked[index] = true;
FileSelected.Add(FileDisplay[index]);
}
The dependencyproperty :
public static readonly DependencyProperty FileSelectedProperty =
DependencyProperty.Register("FileSelected", typeof(ObservableCollection<string>), typeof(FileSelector));
[Bindable(true)]
public ObservableCollection<string> FileSelected
{
get { return (ObservableCollection<string>)this.GetValue(FileSelectedProperty); }
set { this.SetValue(FileSelectedProperty, value); }
}
Also, FileChecked is an observablecollection to keep track of which element is checked or not for later use.
Everything compiled fine but when I check one of the togglebutton this error is thrown :
Access violation at address 00007FFA2981CC85. Read of address 0000000000000000.
It is worth noting that if I changed the DependencyProperty FileSelected to a simple observablecollection, there is no problem, but I don't want to do that as I could not bind it later on.
Any idea why ? thank you
EDIT----
After some more testing with visualstudio debugger, I found that whenever I click on a togglebutton, the dependencyproperty named "FileSelected" is null, while it should be added "FileDisplay[index]" even if I replace the latest by any kind of string...
I'm a little confused. You say this:
It is worth noting that if I changed the DependencyProperty FileSelected to a simple observablecollection, there is no problem, but I don't want to do that as I could not bind it later on.
But in your sample, FileSelected IS an ObservableCollection. What data type was FileSelected originally intended to be?
After some more testing with visualstudio debugger, I found that whenever I click on a togglebutton, the dependencyproperty named "FileSelected" is null, while it should be added "FileDisplay[index]" even if I replace the latest by any kind of string...
There's no way of telling why FileSelected is null without knowing its true data type. Is there only one file selected at a time or multiple?
If FileSelected is supposed to be an OC, it's probably null because you haven't a) initialized it anywhere, or b) you didn't initialize it properly.
Can you post a sample of your initialization?

WPF: Disable items in bounded ItemsControl

I'm working on a WPF page with the following:
<ItemsControl ItemsSource="{Binding Peopl.PhoneNums}" x:Name="PhoneList">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Grid>
<StackPanel Orientation="Horizontal" Margin="0,0,0,0" x:Name="PhoneEntry">
<TextBlock Text="123-456-78901"/>
<ComboBox ...>
</StackPanel>
</Grid>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
There can be multiple stackpanels, each with a unique phone number; in code behind, each phone number has a flag indicating if it should be enabled; I want to be able to enable each entry in the stack panel based on that flag but I'm stuck accessing it....
I have:
foreach (Phone phone in PhoneList.ItemsSource)
{
if (phone.ShouldBeDisabled)
{
int index = PhoneList.Items.IndexOf(phone);
PhoneList.IsEnabled = false;
//this disables the entire control;
// I can't access "PhoneEntry" here... hmm
}
}
Is there a way to disable only one entry at a time? How can I access PhoneEntry? Should I try to disable the each stackpanel entry based on the bound value?
You may invert your view model property and call it ShouldBeEnabled. Now you can bind the StackPanel's IsEnabled property.
<StackPanel ... IsEnabled="{Binding ShouldBeEnabled}">
...
</StackPanel>
In case you can't change the view model, you may use a binding converter that inverts the property value:
<StackPanel ... IsEnabled="{Binding ShouldBeDisabled,
Converter={StaticResource InverseBooleanConverter}}">
...
</StackPanel>
Your Phone class would have to implement the INotifyPropertyChanged interface and fire the PropertyChanged event when the value of the ShouldBeDisabled property changes.

Access the same data a textbox in the XAML is bound to

WPF MVVM in ViewModel I want to access the same data a textbox in the XAML is bound to
The XAML on MainWindow.xaml has a textbox bound to StoredProcs/ProcName
<TextBox Name="txtProcName" Text="{Binding Path=StoredProcs/ProcName}"></TextBox>
And a Grid bound to StoredProcs
Whenever the grid selection changes, the bound text in the textbox changes as it should.
<DataGrid AutoGenerateColumns="False"
Height="300" Width="290"
HorizontalAlignment="Center"
Name="dataGrid1"
VerticalAlignment="Top"
ItemsSource="{Binding StoredProcs}"
IsSynchronizedWithCurrentItem="True"
Margin="-6,0" Grid.RowSpan="2" Grid.Row="0">
<DataGrid.Columns>
<DataGridTextColumn Header="Proc Name" Binding="{Binding ProcName}" >
</DataGridTextColumn>
</DataGrid.Columns>
</DataGrid>
I have a button that executes a procedure in MainWindowViewModel when clicked, that works
<Button Content="Create RDL" Command="{Binding CreateStoredProcedure}" />
In the CreateStoredProcedure code, I need to access the same data that is displayed in the textbox (not using code behind). I would think I need to get the StoredProcs/ProcName but can't figure out how to do that.
I tried adding CommandParameter to the XAML but don't know how to access it in the CreateStoredProcedure instructions as it won't allow me to add paramaters
void CreateStoredProcedureExecute()
{
string procName = "proc";
//procName = { StoredProcs/ProcName };
MessageBoxResult result =
MessageBox.Show(String.Format("Create Stored Procedure {0}", procName));
}
bool CanCreateStoredProcedure()
{
return true;
}
public ICommand CreateStoredProcedure
{
get
{
return new RelayCommand(CreateStoredProcedureExecute,
CanCreateStoredProcedure);
}
}
Unless I am misunderstanding your question, you should be able to get the value of the property that the TextBox is bound to from within CreateStoredProcedure.
One thing though, if you want the TextBox to update the property, make sure you add "Mode=TwoWay" to your binding expression:
<TextBox Name="txtProcName" Text="{Binding Path=StoredProcs/ProcName, Mode=TwoWay}"></TextBox>
Unless I misunderstood I think you want something like this?
<Button
Content="Create RDL"
Command="{Binding CreateStoredProcedure}"
CommandParameter="{Binding ElementName=txtProcName, Path=Text}"/>
However as the other answer states, you should be able to just access the property in the ViewModel that is backing the textbox from the command, but if for some reason you cannot my code should work as well.
(Assuming you are defining RelayCommand as defined by this MSDN article, this should fix your other problem)
public ICommand CreateStoredProcedure
{
get
{
return new RelayCommand<object>(
(object parameter) => CreateStoredProcedureExecute(parameter),
(object parameter) => CanCreateStoredProcedure);
}
}
private void CreateStoredProcedureExecute(object parameter)
{
string ProcName = parameter as string;
}
I will admit my somewhat inexperience with setting up commands like this, but I did find a working example in my code that followed this, so hopefully it helps.
I think KDiTraglia has the right solution. The only thing I would do differently is to bind the CommandParameter to the model, not the UI element.
<Button
Content="Create RDL"
Command="{Binding CreateStoredProcedure}"
CommandParameter="{Binding Path=StoredProcs/ProcName}" />
I'm assuming that StoredProcs/ProcName is a placeholder for a real, valid binding path.
Root around here for more information: http://msdn.microsoft.com/en-us/library/ms752308

Drag Event Won't Fire for ListBox in Silverlight

Hopefully this is an easy question. I'm using the ListBoxDragDropTarget from the Silverlight Toolkit to set up drag and drop from one ListBox to another. I can't seem to get the event to fire. Here's my XAML code:
<toolkit:ListBoxDragDropTarget HorizontalAlignment="Left"
HorizontalContentAlignment="Stretch"
VerticalAlignment="Top"
VerticalContentAlignment="Stretch"
Margin="39,117,0,0"
Grid.Row="1"
AllowDrop='True'>
<ListBox x:Name='columnHeadings'
MinHeight='100'
MinWidth='100'>
</ListBox>
</toolkit:ListBoxDragDropTarget>
<toolkit:ListBoxDragDropTarget AllowDrop='True'
Grid.Column='1'
Grid.Row='1'
HorizontalContentAlignment="Stretch"
VerticalContentAlignment="Stretch"
VerticalAlignment="Center"
HorizontalAlignment="Left">
<ListBox x:Name='customerFields'
Grid.Column='1'
Grid.Row='1'
Visibility='Collapsed'
Drop='CustomerFieldsDrop'>
</ListBox>
</toolkit:ListBoxDragDropTarget>
And here's my event handler in the code behind page:
private void CustomerFieldsDrop(object sender, DragEventArgs e)
{
MessageBox.Show(string.Format("Something was dropped!"));
}
As you can see, I was aiming for something real simple. I tried assigning an event handler to the parent ListBoxDragDropTarget for the customerFields List Box; ironically, that worked.
My purpose here is to allow a user to import a text file and get a listing of the file's column headers in one List Box and then "connect" them to data fields listed in the second List Box. So no list reordering or moving items from one list to another.
The columnHeadings.ItemsSource property is a string[] object. The customerFields.ItemsSource property is an IEnumerable<string> object.
Any insight would be appreciated.
I think the AllowDrop="True" and Drop="EventName" properties need to be within the same element to work. You probably have to set the AllowDrop property to "True" in the ListBox itself:
<ListBox x:Name="customerFields"
Grid.Column="1"
Grid.Row="1"
Visibility="Collapsed"
Drop="CustomerFieldsDrop"
AllowDrop="True"
</ListBox>
Or add the Drop="CustomerFieldsDrop" property to the ListBoxDragDropTarget tag:
<toolkit:ListBoxDragDropTarget AllowDrop='True'
Grid.Column='1'
Grid.Row='1'
HorizontalContentAlignment="Stretch"
VerticalContentAlignment="Stretch"
VerticalAlignment="Center"
HorizontalAlignment="Left"
Drop="CustomerFieldsDrop">
Either one should work...

Getting value back from ListBox to EventHandler in WP7

In my WP7 application I have ListBox control that binds with List<ItemTemplate> collection. On each ListBoxItem I have Click event which navigates to DisplayItem.xaml. Each ItemTemplate object has Id property which has to be passed to DispalyItem.xaml. I know that I can pass this value from Click EventHandler to DisplayItem.xaml using QueryString but how do I pass it from ListBox item to EventHandler ?
<ListBox x:Name="listBoxItems">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<Ellipse Fill="red" Width="30" Height="30"></Ellipse>
<StackPanel Orientation="Horizontal">
<CheckBox IsChecked="{Binding Status}" FontSize="35" />
</StackPanel>
<StackPanel Orientation="Horizontal">
<HyperlinkButton Content="{Binding ItemContent}" Name="itemButton" Click="itemButton_Click"/>
</StackPanel>
<toolkit:ContextMenuService.ContextMenu>
<toolkit:ContextMenu>
<toolkit:MenuItem Header="edit"/>
<toolkit:MenuItem Header="delete"/>
</toolkit:ContextMenu>
</toolkit:ContextMenuService.ContextMenu>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
Id property is not mentioned in the above code because I just simply didnt know where to place it. Generally I want to know how can I get Id property back to the click EventHandler ? I'm sorry if this question is basic for you but I'm new to that and i wasn't sure how to google that.
If you're really new to Windows Phone 7, you may want to stop using the "Click" event and instead use the ListBox.SelectionChanged event. If you are bound to List<MyObject>, you could do the following:
In your XAML:
<ListBox SelectionChanged="NavigateToMyDetail" ... >
Then in the code behind, you would have something like this:
private void NavigateToMyDetail(object sender, SelectionChangedEventArgs e)
{
// Make sure that the ListBox change wasn't due to a deselection
if(e.AddedItems != null && e.AddedItems.Count == 1)
{
MyObject selectedItem = (MyObject)e.AddedItems[0];
// Now you have access to all your MyObject properties
// and you can pass that to your new page as a parameter
NavigationService.Navigate(new Uri("DisplayItem.xaml?ItemID=" + selectedItem.id.ToString(), UriKind.Relative));
}
}
And you can get that ID with the following code (probably in your "OnNavigatedTo" method).
string myItemID = null;
if(this.NavigationContext.QueryString.ContainsKey("ItemID"))
myItemID = NavigationContext.QueryString["ItemID"];
Hope that helps. The other way to try to get it is to give your ListBox a x:Name and then references it in your Click handler like:
private void MyClickHandler(object sender, System.Windows.RoutedEventArgs e)
{
MyObject selectedObject = (MyObject)MyListBoxName.SelectedItem;
}
There is a much simpler solution if you use data binding with an MVVM viewmodel behind it.
Simply bind you view to a property in the view model for the listbox "Source" and then also do the same for the ListBox "SelectedItem" or "SelectedIndex" properties, then you will have all you need accessible where ever you needed.
Only think to be aware of (as I'm uncertain if it ever got fixed) is to fixed the selected index property when an item has been selected, if you do not reset it to -1 then if the user returns to the list they cannot select the same item. (do this in the codebehind for the click event)
Also if you use MVVM and databinding you can enact an action from the change of the Selected item rather than using Code behind to drive the direction, always an option to keep things simple (but not mandatory)
I have also came to my own solution. I'm not sure If its correct bit its certainly solving my problem for now.
I found this CommandParameter property of object HyperlinkButton. I bound my MyObject.Id property value to it.
<HyperlinkButton Content="{Binding ItemContent}" Click="itemButton_Click" CommandParameter="{Binding Id}" />
Then in my EventHandler i said:
private void itemButton_Click(object sender, RoutedEventArgs e)
{
HyperlinkButton butt = sender as HyperlinkButton;
NavigationService.Navigate(new Uri("/ViewItem.xaml?itemId=" + butt.CommandParameter.ToString(), UriKind.Relative));
}
It works as I need it to work but I'm not sure If i should use it in my applications in the future.

Categories