I have a ListBox for a few items, and I need to be able to click them. Problem is, the SelectionChanged event doesn't get fired when I click on the item's text, only if I click on the blank part. I'm quite new to WPF, and I don't understand why this is happening.
XAML:
<ListBox Name="lBoxVouchers" BorderThickness="0" FontSize="15" SelectionChanged="lBoxVouchers_SelectionChanged">
<ListBox.ItemTemplate>
<DataTemplate>
<ListBoxItem Content="{Binding Name}" />
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
Handler:
private void lBoxVouchers_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
if (e.AddedItems.Count > 0)
MessageBox.Show("You just selected " + e.AddedItems[0]);
}
I'm binding the list of objects in code via the lBoxVouchers.ItemsSource property, and they show up. Each object has a Name property, of course.
I've tried setting IsEnabled on the ListBox and the items, both in code and XAML, but it doesn't help.
Any comments about better ways to do this in WPF are also welcome.
If you only want to show the Name property you could define your listbox like this:
<ListBox Name="lBoxVouchers" BorderThickness="0" FontSize="15" SelectionChanged="lBoxVouchers_SelectionChanged" DisplayMemberPath="Name" />
If you put your items on an ObservableCollection on code-behind, you can also pass the databinding to XAML:
<ListBox Name="lBoxVouchers" BorderThickness="0" FontSize="15" SelectionChanged="lBoxVouchers_SelectionChanged" DisplayMemberPath="Name" ItemsSource={Binding Path=Items}" />
And on your code behind you should have something like:
ObservableCollection<object> Items {get; set}
About the handler, I would also do something like this:
private void lBoxVouchers_SelectionChanged(object sender, SelectionChangedEventArgs e) {
if (((ListBox)sender).SelectedItem != null)
MessageBox.Show("You just selected " + (ListBox)sender).SelectedItem);
}
Set IsSynchronizedWithCurrentItem="true" on the listbox.
Here you can find a starting point to get more details about this property.
Setting this property to true makes the selection be in sync with the current item which holds the actual selected item. When you click the blank space probably the current item changes to null and you get your event handler called.
may be the content in the listbox item is not stretched. just write this style for the listbox item and try.
<Style TargetType="{x:Type ListBoxItem}">
<Setter Property="HorizontalContentAlignment" Value="Stretch" />
</Style>
Related
I have the following ListBox
<ListBox x:Name="lbListItems"
<ListBox.ItemTemplate>
<DataTemplate>
<ToggleButton x:Name="btnItem">
<StackPanel Orientation="Horizontal" Width="956">
<toolkit:AutoCompleteBox x:Name="acbItem"</toolkit:AutoCompleteBox>
</StackPanel>
</ToggleButton>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
How can I set the ItemsSource for acbItem in code behind ?
I cant access to it !
Use Loaded event is a solution.
<toolkit:AutoCompleteBox Loaded="myControl_Loaded" ...
private void myControl_Loaded(object sender, RoutedEventArgs e)
{
toolkit:AutoCompleteBox myCombo = sender as toolkit:AutoCompleteBox;
// Do things..
}
But it's better to use MVVM approach to set ItemsSource from XAML
I bind the ItemsSource property for the Listbox but it does not work, If I put the Listbox out of template it works !
Then you should specify a RelativeSource of the binding:
<toolkit:AutoCompleteBox x:Name="acbItem"
ItemsSource="{Binding DataContext.YourItemsSourceCollection, RelativeSource={RelativeSource AncestorType=ListBox}}" />
Simply replace "YourItemsSourceCollection" with the actual name of the propertty to bind to and keep the rest.
I have seen many questions similar to mine but none that solves or addresses my problem.
What I have is a group of toggle buttons that exist inside of an ItemsControl, and these toggle buttons are created dynamically at run time based off of the number of people inside of an index that the ItemSource of the ItemsControl is bound to. When these toggle buttons are clicked in the UI, some filtering is done based on the person's name. Multiple buttons or even all the buttons can be selected if so desired. There is also a "clear" button which simply removes all filtering and should reset the toggle button IsClicked states.
My problem is that when I click the clear button, all of the toggle buttons that were clicked remain clicked. I am trying to have the clear button reset every ToggleButton's IsClicked value to false by binding the IsClicked value to a property that is managed in the code, but no matter what I try I cannot get this to work.
Code:
Xaml
<Button Content="Clear Filter" HorizontalAlignment="Center" Margin="0,0,0,1" VerticalAlignment="Bottom" Grid.Column="1" Click="ClearClick"/>
<ItemsControl ItemsSource="{Binding Source={StaticResource peopleSorted}}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel Orientation="Horizontal"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<ToggleButton Name="toggleButton" Margin="0,0,5,0" Command="ApplicationCommands.Open"
CommandParameter="{Binding Name}" IsChecked="{Binding IsChecked, Mode=TwoWay}">
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Name}" />
</StackPanel>
</ToggleButton>
</DataTemplate>
</ItemsControl.ItemTemplate>
c# ViewModel:
private void ClearClick(object sender, RoutedEventArgs e)
{
this.m_viewModel.ClearFiltering();
}
C# model:
public bool IsChecked
{
get
{
return this.m_isChecked;
}
set
{
this.m_isChecked = value;
this.OnPropertyChanged("IsChecked");
}
}private bool m_isChecked;
public void ClearFiltering()
{
m_hashTable.Clear();
this.IsChecked = false;
}
I am pretty stuck. I feel like the reason the binding isn't working is that the toggle buttons are dynamically created inside of an ItemsControl, but I am not sure if that's what's wrong or how to fix it if it is.
UPDATE
I was able to work around this by doing a swap and swap-back on the data of observable collection that the ToggleButtons ItemsControl is bound to, which triggered the OnPropertyChanged for the entire collection of toggle buttons, thus resetting that part of the UI. It is a simple hack with almost no overhead and four lines of code, but doesn't really answer the original question.
I have a TextBox inside a ListBoxItem which is disabled so I can drag and drop it in the ListBox.
Once I double click it I want it to be Enabled so I can edit the Text and when I'm done I want it do be Disabled again to do drag and drop.
I have the MouseDoubleClick event on the ListBoxItem but it doesn't change the TextBox ReadOnly. Can anybody tell me how to achieve this.
at the moment TextBox returns null. seems like I don't get access to it the way I'm trying.
XAML
<ListBox Name="Locations" Cursor="Hand" HorizontalAlignment="Left" Height="351" Margin="10,48,0,0" VerticalAlignment="Top" Width="285" ItemsSource="{Binding Locations}" dd:DragDrop.IsDragSource="True"
dd:DragDrop.IsDropTarget="True" SelectedItem="{Binding SelectedItem}">
<ListBox.ItemContainerStyle>
<Style TargetType="{x:Type ListBoxItem}" BasedOn="{StaticResource {x:Type ListBoxItem}}">
<EventSetter Event="MouseDoubleClick" Handler="ListBoxItem_MouseDoubleClick"/>
</Style>
</ListBox.ItemContainerStyle>
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBox Name="textBox" Text="{Binding Path=Name}" IsHitTestVisible="False" Width="270" Background="Transparent" BorderThickness="0" Margin="2"/>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
In View
private void ListBoxItem_MouseDoubleClick(object sender, MouseButtonEventArgs e)
{
ListBoxItem item = sender as ListBoxItem;
textBox.IsReadOnly = false;
}
That's because the textbox "doesn't exists" in the visual tree, WPF creates one for each listitem when needed, so you never have a reference to it. It is possible to navigate the visual tree and get the textbox reference but I advise against it, instead, create a property in your item model, something like "EditMode" that when you set it to true, the textbox will enable through the binding of the IsEnabled property.
My listbox is binded to an items source and selecteditem property is also binded. Most of my work is being done in selecteditem property. Actually I have two listboxes, for each item in first list box there are some child items in the collection. Against all the items that are selected in first listbox, their child items are supposed to be added in the second list box.
Problem is Selecting the item(by checking the checkbox) does not raise the SelectedItem property changed
XAML for my listbox controls are
<ListBox SelectionMode="Multiple" ItemsSource="{Binding Charts,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}" SelectedItem="{Binding SelectedChart, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}">
<ListBox.ItemTemplate>
<DataTemplate>
<CheckBox Content="{Binding ChartName}" VerticalAlignment="Center" IsChecked="{Binding IsChartSelected, Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}"/>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
<ListBox ItemsSource="{Binding Tracks, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}">
<ListBox.ItemTemplate>
<DataTemplate >
<StackPanel Orientation="Horizontal">
<CheckBox VerticalAlignment="Center" IsChecked="{Binding IsTrackSelected}"/>
<TextBlock Margin="5 0 0 0" VerticalAlignment="Center" Text="{Binding TrackName}"/>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
My Chart Selection Changed property in view model is
public ChartSourceForMultipleSelection SelectedChart
{
get { return _selectedChart; }
set
{
_selectedChart = value;
ChartSelectionChanged();
NotifyPropertyChanged("SelectedChart");
}
}
This binding doesn't make any sense:
<CheckBox IsChecked="{Binding IsTrackSelected,
RelativeSource={RelativeSource AncestorType={x:Type ListBoxItem}}}" />
This attempts to bind to a property IsTrackSelected on the outer ListBoxItem, but no such property exists. It doesn't look like you need a RelativeSource here, provided IsTrackSelected is a property of the underlying data item.
Also, I'm not sure why you're a ListBox for the tracks collection; it looks like your concepts of track selection is separate from the ListBox concept of a selected item. Why not use a simple ItemsControl instead?
As to your primary problem, the CheckBox in the item template is eating the mouse/focus events that would normally tell the ListBox to select the parent ListBoxItem when clicked. You can manually update the selection state of a ListBoxItem whenever its inner CheckBox receives keyboard focus.
Add GotKeyboardFocus="OnChartCheckBoxGotKeyboardFocus" to the CheckBox your chart list item template, give the chart list box a name, e.g., x:Name="ChartsList", and write the handler in the code behind:
private void OnChartCheckBoxGotKeyboardFocus(
object sender,
KeyboardFocusChangedEventArgs e)
{
var checkBox = sender as CheckBox;
if (checkBox == null)
return;
var listBoxItem = ItemsControl.ContainerFromElement(ChartsList, checkBox)
as ListBoxItem;
if (listBoxItem != null)
listBoxItem.IsSelected = true;
}
I have a combobox and I want to prevent the user from scrolling through the items with the mousewheel.
Is there an easy way to do that?
(C#, VS2008)
Use the MouseWheel event for your ComboBox:
void comboBox1_MouseWheel(object sender, MouseEventArgs e) {
((HandledMouseEventArgs)e).Handled = true;
}
Note: you'll have to create event in code:
comboBox1.MouseWheel += new MouseEventHandler(comboBox1_MouseWheel);
For WPF, handle the PreviewMouseWheel event instead.
It would also be a good idea to consider ComboBox.IsDropDownOpen so the user can still use mouse scroll if there are a lot of items in the selection when the ComboBox is expanded.
Another thing is to apply the same behavior across the whole application.
I usually do all the above using the following code:
App.xaml
<Application.Resources>
<Style TargetType="ComboBox">
<EventSetter Event="PreviewMouseWheel" Handler="ComboBox_PreviewMouseWheel" />
</Style>
</Application.Resources>
App.xaml.cs
private void ComboBox_PreviewMouseWheel(object sender, System.Windows.Input.MouseWheelEventArgs e)
{
e.Handled = !((System.Windows.Controls.ComboBox)sender).IsDropDownOpen;
}
I use another solution that also works on Mono.
Goal is to prevent accidentally scrolling (that is when the user is not looking at the comboBox when using the mouse wheel). If he / she scroll outside the visible portion of comboBox , the combo box should not scroll, otherwise it should.
My solution:
Place a read only text box outside the visible portion of the screen. In form_load I placed the line: hiddenTextbox.left = -100 ;
Set the focus to this text box when the mouse leaves the combo box using mouse leave event. In comboBox1_MouseLeave I placed the line: hiddenTextbox.focus();
Handle mouseWheel event: From1.MouseWheel += Form1_MouseWheel;
textBoxHidden.MouseWheel += Form1_MouseWheel;
My Combobox's were placed inside a DataGrid [C#, WPF XAML] just like this:
<DataGrid x:Name="dgvFieldsMapping" Grid.Row="1" ItemsSource="{Binding}">
<DataGrid.Columns>
...
<DataGridTemplateColumn Width="*" Header="Destination Field" >
<DataGridTemplateColumn.CellTemplate >
<DataTemplate >
<ComboBox ItemsSource="{Binding Source={StaticResource CustomerDbFields}}" SelectedValue="{Binding destinationField, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" ></ComboBox>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
...
</DataGrid.Columns>
</DataGrid>
So whenever a DropDown was closed after selecting a Value, the Mousewheel would scroll that Combobox's Items and modify my Selection.
I ended up modifying my XAML to look like this:
<DataGrid x:Name="dgvFieldsMapping" Grid.Row="1" ItemsSource="{Binding}">
<DataGrid.Resources>
<Style x:Key="dgvComboBox_Loaded" TargetType="ComboBox">
<EventSetter Event="Loaded" Handler="dgvCombobox_Loaded" />
</Style>
</DataGrid.Resources>
<DataGrid.Columns>
...
<DataGridTemplateColumn Width="*" Header="Destination Field" >
<DataGridTemplateColumn.CellTemplate >
<DataTemplate >
<ComboBox Style="{StaticResource dgvComboBox_Loaded}" ItemsSource="{Binding Source={StaticResource CustomerDbFields}}" SelectedValue="{Binding destinationField, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" ></ComboBox>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
...
</DataGrid.Columns>
</DataGrid>
And adding these lines in codebehind
public void dgvCombobox_Loaded(Object sender, RoutedEventArgs e)
{
((ComboBox)sender).DropDownClosed -= ComboBox_OnDropDownClosed;
((ComboBox)sender).DropDownClosed += new System.EventHandler(ComboBox_OnDropDownClosed);
}
void ComboBox_OnDropDownClosed(object sender, System.EventArgs e)
{
dgvFieldsMapping.Focus();
}
In this way I just move the Focus away from the ComboBox to the outer DataGrid after closing its corresponding DropDown, so I don't need to add any dummy FrameWorkElement