Identifying a button in a templated ListBox - c#

I have a ListBox that has a custom DataTemplate assigned to it, so that it can correctly display it's content - a custom "Accessory" (which consists of three string properties) object per row. Additionally, there is a button on each row. That button should trigger an event that adds the selected accessory to a MemoryList. Here is the DataTemplate:
<DataTemplate x:Key="AccessoryListBoxTemplate">
<StackPanel>
<!--Truncated-->
<TextBlock FontFamily="Avenir Next LT Pro" VerticalAlignment="Center" FontSize="14" Text="{Binding Path=AgilityHeader}" Margin="3,0,0,0" Grid.Column="0" />
<TextBlock FontFamily="Avenir Next LT Pro" VerticalAlignment="Center" FontSize="14" Text="{Binding Path=ItemNumber}" Grid.Column="1" />
<TextBlock FontFamily="Avenir Next LT Pro" HorizontalAlignment="Right" VerticalAlignment="Center" Text="{Binding Path=Price}" FontSize="14" Grid.Column="2" />
<Button x:Name="ButtonAccessoryAddToMemoryList" VerticalAlignment="Center" Click="buttonAccessoryAddToMemoryList_Click" HorizontalAlignment="Right" FontSize="14" Width="80" Grid.Column="3" Margin="0,5,0,5">Minneslista</Button>
</Grid>
</StackPanel>
</StackPanel>
</DataTemplate>
And here is the ListBox:
<ListBox Grid.ColumnSpan="3" Grid.Row="1" BorderThickness="0" x:Name="ListBoxAccessories" ItemTemplate="{StaticResource AccessoryListBoxTemplate}" HorizontalContentAlignment="Stretch" ItemsSource="{Binding}" SelectedIndex="-1" IsEnabled="True" />
The problem I'm having is this - I cannot reliably identify on which row ButtonAccessoryAddToMemoryList is clicked, since the row that the button is on is not set as the SelectedItem for the ListBox if the user doesn't first select the row and then push the button - and honestly, who does that? :)
How should I go about identifying which button was pressed? Any help would be greatly appreciated. Thanks!
[EDIT] Thanks to Chadwick for that answer. Works perfectly. [/EDIT]

If what you're really after is to know which Accessory object was clicked on you can set the Tag property of the button:
<Button x:Name="ButtonAccessoryAddToMemoryList" Tag="{Binding}" Click="buttonAccessoryAddToMemoryList_Click" ... >Minneslista</Button>
and then cast out the object in the click handler:
private void ButtonAccessoryAddToMemoryList(object sender, RoutedEventArgs e)
{
Button b = e.Source as Button;
Accessory a = b.Tag as Accessory;

Try out M-V-VM pattern and a command binding. If the datatemplate were bound to its own object and the command were activated, then you would already know the record being clicked.
In the XAML, you could give the button a _Loaded event handler. Then, in the code behind, set the _Click event. Perhaps make an array of delegates so that the clicks will call different handlers.

Related

WPF find clicked button closest parent (TabItem) and close it

I am wondering whether there is a better simpler way rewrite the following code find the closest parent which is a TabItem and remove it from the TabControl.
I have a TabControl where I add new TabItems dynamically. I assign a HeaderTemplate to each tab which looks like this;
<DataTemplate x:Key="AttorneyTabHeader">
<StackPanel Orientation="Horizontal">
<TextBlock Text="THE title" Margin="2,0,0,0" FontSize="16" VerticalAlignment="Center" />
<Button Width="Auto" UseLayoutRounding="False" BorderBrush="Transparent" Background="Transparent" Click="CloseAttorneysTabButtonClick">
<Image Source="/images/close-cross-thin-circular-button/close-cross-thin-circular-button16.png" Height="16"></Image>
</Button>
</StackPanel>
</DataTemplate>
The header has a close button and I would like to close the TabItem whenever the button is clicked. My click handler looks like this;
public void CloseAttorneysTabButtonClick(object sender, RoutedEventArgs e)
{
TabItem this_tab = (TabItem)((Button)sender).Parent.GetParentObject().GetParentObject().GetParentObject().GetParentObject().GetParentObject().GetParentObject();
AttorneysTabControl.Items.Remove(this_tab);
}
I am wondering whether there is a better way to rewrite this because now I am depending on getting the parent over and over again suppose I change the button and forget changing the handler.
There's probably a few ways you can handle it, but the simplest is likely to bind to the TabItem in the Tag property for the Button so that you can use it in your event handler.
<DataTemplate x:Key="TabHeaderTemplate">
<StackPanel Orientation="Horizontal">
<TextBlock Text="The Title" Margin="2 0 0 0" FontSize="16" VerticalAlignment="Center" />
<Button Width="Auto" UseLayoutRounding="False"
BorderBrush="Transparent" Background="Transparent"
Tag="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type TabItem}}, Mode=OneWay}"
Click="Button_Click">
<Image Source="images/close.png" Height="16" />
</Button>
</StackPanel>
</DataTemplate>
Now your event handler can be relatively simple, and doesn't need to know as much as it does in your example.
void Button_Click(object sender, RoutedEventArgs e)
{
if (sender is Button button && button.Tag is TabItem item) {
var tabControl = (TabControl)item.Parent;
tabControl.Items.Remove(item);
}
}

Is it possible to type in dashes if I have a TextBox that is a Child of a Checkbox?

So I have this currently:
<CheckBox x:Name="checkBox" HorizontalAlignment="Left" Margin="346,17,0,0" Grid.Row="1" VerticalAlignment="Top" Width="295" Height="189" >
<TextBox x:Name="textBox" HorizontalAlignment="Left" Height="20" Grid.Row="1" TextWrapping="Wrap" VerticalAlignment="Top" Width="120"/>
</CheckBox>
I currently can not type in a "-" in my TextBox because it is a child of my CheckBox and CheckBox has some built in feature where if the +/= key OR -/_ key is pressed it checks/unchecks my CheckBox. I don't want this feature, I want the user to be able to actually type in dashes like "-" without it unchecking/checking my checkbox. Is there a easy way to get around this?
Thanks in advance
Wow, after hours of losing my mind I figured out how to get rid of this problem.
All you have to do is set IsThreeState = True; because they are doing a check for IsThreeState in the CheckBox.cs code here
One way is to create a custom checkbox and override OnKeyDown:
public class MCheckBox : CheckBox
{
protected override void OnKeyDown(KeyEventArgs e) { }
}
Another way is to just not put the TextBox within the CheckBox, but beside it, both in a DockPanel with some modifications:
<StackPanel>
<CheckBox>
<TextBox Text="this is a TextBox within a CheckBox"/>
</CheckBox>
<DockPanel LastChildFill="False">
<CheckBox VerticalAlignment="Center"/>
<TextBox Text="this is a TextBox beside a CheckBox" Margin="4,0,0,0"/>
</DockPanel>
</StackPanel>

Telerik RadComboBox Apply SelectionBoxTemplate OnLoad?

Basically, we have created our own custom MultiSelect-ComboBox using Telerik's RadComboBox and the SelectionBoxTemplate to make the custom text appear when multiple items are selected.
<ucControls:MultiSelectComboBoxBase
... >
<ucControls:RadComboBox
x:Name="RadCombo"
Text=""
Height="22"
HorizontalContentAlignment="Left"
HorizontalAlignment="Stretch"
DropDownClosed="RadCombo_DropDownClosed"
IsTextSearchEnabled="False"
IsEnabled="{Binding IsEnabled}"
CanAutocompleteSelectItems="False"
CanKeyboardNavigationSelectItems="False"
LostFocus="RadCombo_LostFocus">
<ucControls:RadComboBox.SelectionBoxTemplate>
<DataTemplate>
<Grid Background="Aqua">
<TextBlock x:Name="ComboBoxDisplay" Text="{Binding Text, ElementName=RadCombo}" />
</Grid>
</DataTemplate>
</ucControls:RadComboBox.SelectionBoxTemplate>
<ucControls:RadComboBox.ItemTemplate>
<DataTemplate>
<StackPanel>
<CheckBox x:Name="chkBox"
Content="{Binding ItemValue}"
Tag="{Binding ItemID}"
Height="16" Margin="2"
IsChecked="{Binding IsChecked, Mode=TwoWay}"
Visibility="{Binding IsSelectAllItem, Converter={StaticResource booleanToVisibilityConverter}, ConverterParameter=NOT}"
HorizontalAlignment="Stretch" VerticalAlignment="Top"
Checked="ChkBox_Checked" Unchecked="ChkBox_Unchecked"
IsEnabled="{Binding IsEnabled, Mode=TwoWay}"/>
<HyperlinkButton
VerticalAlignment="Center" Content="{Binding ItemValue}"
Visibility="{Binding IsSelectAllItem, Converter={StaticResource booleanToVisibilityConverter}}"
Style="{StaticResource HyperlinkButtonNoBorderStyle}" Margin="3,2,0,0"
Click="HyperlinkButton_Click"
IsEnabled="{Binding IsEnabled, Mode=TwoWay}" />
</StackPanel>
</DataTemplate>
</ucControls:RadComboBox.ItemTemplate>
</ucControls:RadComboBox>
It works great, but the only problem is that it appears that the SelectionBoxTemplate isn't actually applied when the radcombobox is loaded. It only applies when the box is clicked.When it loads it displays:
Then you click and it displays the dropdown:
Then you click away and it displays the correct text from the SelectionBoxTemplate:
Note I made the background color of the Textblock blue, just to show that it does not apply on the initial load.Ideally, when it loads it should automatically apply the SelectionBoxTemplate and display the correct information rather than having the click and then click away from the multiselectcombobox. Also, the TextBlock text is bound to the RadComboBox's text because in the code behind we set the RadComboBox's text. There is no issue with null values or the Text not being set before because I've looked in debugging mode and all the information is there, the selection box template is just not being applied until the box receives focus. Is there some sort of event that has to fire off before the template is applied, or is there someway I can force the template to be applied onload?
I had the same issue as you. I've managed to solve it by setting the SelectedIndex="0"
I've also read that the SelectionBoxTemplate only works when the RadComboBox is not editable: IsEditable="False"
<ucControls:RadComboBox
x:Name="RadCombo"
SelectedIndex="0"
IsEditable="False"
Height="22"
HorizontalContentAlignment="Left"
HorizontalAlignment="Stretch"
DropDownClosed="RadCombo_DropDownClosed"
IsEnabled="{Binding IsEnabled}"
LostFocus="RadCombo_LostFocus">
...
</ucControls:RadComboBox>

Listbox inside the checkbox content - WPF

I have listbox inside the checkbox content. I want the checkbox to be checked whenever any click event happen in the listbox. But the problem it does not check, only clicking on the textblock does check. Any ideas how ?
<CheckBox Checked="orderItemChecked"
Unchecked="orderItemUnchecked"
Grid.Column="0" Grid.Row="0" IsChecked="{Binding Path=Completed}"
HorizontalContentAlignment="Stretch" >
<StackPanel>
<TextBlock Text="{Binding Path=sItemName}" ></TextBlock>
<ListBox Grid.Row="1" HorizontalAlignment="Left" HorizontalContentAlignment="Stretch"
ItemsSource="{Binding Path=aSinglOptns}"
Margin="20,0,0,0"
ItemTemplate="{StaticResource SinglOptnTmpl}"
Style="{StaticResource SheetListStyle}"
ItemContainerStyle="{StaticResource ListBoxItemStyle}"/>
</StackPanel>
</CheckBox>
Try turning the hit testing off from the ListBox:
<ListBox Grid.Row="1" IsHitTestVisible="false" ... />
You can subscribe the event PreviewMouseLeftButtonUp on the CHECKBOX and check it in code behind.

WP8 ListPicker SelectionChanged fired at the beginning

I have a ListPicker with a SelectionChanged Event. I want the SelectionChange event to only fire when I change the selection, obviously, but in my case, it is also firing whenever I load the page.
Code:
<toolkit:ListPicker x:Name="sightingTypesPicker" ItemsSource="{Binding sightingTypes, ElementName=this}" SelectionChanged="sightingTypesPicker_SelectionChanged">
<toolkit:ListPicker.ItemTemplate>
<DataTemplate>
<StackPanel>
<TextBlock Text="{Binding Name}" FontSize="{StaticResource PhoneFontSizeSmall}"/>
</StackPanel>
</DataTemplate>
</toolkit:ListPicker.ItemTemplate>
<toolkit:ListPicker.FullModeItemTemplate>
<DataTemplate>
<StackPanel>
<TextBlock Text="{Binding Name}" FontSize="{StaticResource PhoneFontSizeLarge}"/>
<TextBlock Visibility="Collapsed" Text="{Binding TypeId}" FontSize="{StaticResource PhoneFontSizeSmall}"/>
</StackPanel>
</DataTemplate>
</toolkit:ListPicker.FullModeItemTemplate>
</toolkit:ListPicker>
C#:
private void sightingTypesPicker_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
SightingType selecteditem = e.AddedItems[0] as SightingType;
Sighting.Instance.TypeId = selecteditem.TypeID;
}
Basically by defaut a list picker or a list box or a combo box or any drop down has a particular selected item and that is the zeroth item of the listbox./.. etc. So when the page is loading and data is being populated to the list boxes or else it fires the selection change from -1 ie no dat state to 0 selection changed to default value :)
You can add the handler on the Loaded event handler in your code so you ensure it's only called after that.
Update:
For example add this code in Loaded event of your page instead of XAML:
sightingTypesPicker.SelectionChanged += sightingTypesPicker_SelectionChanged;

Categories

Resources