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.
Related
I am new in c# and wpf. I have a treeveiew and what I need is - when user click on one of the items, mark this item as selected. For example as it is implemented in DataGrid when user click on the row it marks as selected and user can easaly understand which row was selected.
My implementation of TreeView is
<TreeView Name="Tv_request"
Grid.Row="1"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
ItemsSource="{Binding Path=RequestSet}">
<TreeView.ItemTemplate>
<HierarchicalDataTemplate
ItemsSource="{Binding Childrens}"
DataType="{x:Type local:IRequest}">
<TreeViewItem MouseUp="TreeViewItem_MouseUp"
Header="{Binding TypePage}"/>
</HierarchicalDataTemplate>
</TreeView.ItemTemplate>
<TreeView.ItemContainerStyle>
<Style TargetType="{x:Type TreeViewItem}">
<Setter Property="IsExpanded" Value="True" />
</Style>
</TreeView.ItemContainerStyle>
</TreeView>
Way I can get clicked item - by TreeViewItem_MouseUp event, but I need to implement mark selection item.
How to implement it?
EDIT
In order to get clicked item I am using such approach
private void TreeViewItem_MouseUp(object sender, System.Windows.Input.MouseButtonEventArgs e)
{
var tree = sender as TreeViewItem;
if (tree != null)
{
IRequest item = tree.DataContext as IRequest;
Presenter?.TreeViewSelectedItem(item);
}
}
Such way I can have access to my selected item, but if I change it to
private void TreeViewItem_MouseUp(object sender, System.Windows.Input.MouseButtonEventArgs e)
{
var tree = sender as ContentPresenter;
if (tree != null)
{
IRequest item = tree.DataContext as IRequest;
Presenter?.TreeViewSelectedItem(item);
}
}
So, tree.DataContext return me NodeType and logically it is ok, because I set
<ContentPresenter Content="{Binding NodeType}"
MouseUp="TreeViewItem_MouseUp"/>
instead of
<TreeViewItem MouseUp="TreeViewItem_MouseUp"
Header="{Binding NodeType}"/>
So, question is, how I can get entire clicked object not just his NodeType?
Firstly, your item template is creating a TreeViewItem inside a TreeViewItem. It should look like this:
<HierarchicalDataTemplate
ItemsSource="{Binding Childrens}"
DataType="{x:Type local:IRequest}">
<ContentPresenter Content="{Binding TypePage}"/>
</HierarchicalDataTemplate>
The TreeViewItem will be automatically created by WPF, and the HierarchicalDataTemplate you provide will be applied to it.
Secondly, TreeViewItem has a property called IsSelected. You can use the Style to bind this property to a property of your IRequest object:
<Style TargetType="{x:Type TreeViewItem}">
<Setter Property="IsExpanded" Value="True" />
<Setter Property="IsSelected" Value="{Binding RequestIsSelected}" />
</Style>
Or you can use an EventSetter to provide a handler for the TreeViewItem.Selected event if you prefer:
<Style TargetType="{x:Type TreeViewItem}">
<Setter Property="IsExpanded" Value="True" />
<EventSetter RoutedEvent="Selected" Value="TreeViewItem_Selected" />
</Style>
You could also use the TreeView.SelectionChanged event instead of dealing with individual tree view items, but be aware that you (annoyingly!) can't select a new item from the TreeView itself.
If you don't mess things up by nesting a hierarchicaltemplate inside an itemtemplate you don't need to do anything at all to get a selected treeview item coloured.
Look at the xaml here:
https://learn.microsoft.com/en-us/dotnet/api/system.windows.hierarchicaldatatemplate?view=netcore-3.1
<HierarchicalDataTemplate DataType = "{x:Type src:League}"
ItemsSource = "{Binding Path=Divisions}">
<TextBlock Text="{Binding Path=Name}"/>
</HierarchicalDataTemplate>
<HierarchicalDataTemplate DataType = "{x:Type src:Division}"
ItemsSource = "{Binding Path=Teams}">
<TextBlock Text="{Binding Path=Name}"/>
</HierarchicalDataTemplate>
<DataTemplate DataType="{x:Type src:Team}">
<TextBlock Text="{Binding Path=Name}"/>
</DataTemplate>
Notice what you give it as a template appears inside a treeviewitem. Not hierarchicaldatatemplate within itemtemplate.
Similarly, here: https://www.wpf-tutorial.com/treeview-control/treeview-data-binding-multiple-templates/
<TreeView Name="trvFamilies">
<TreeView.Resources>
<HierarchicalDataTemplate DataType="{x:Type self:Family}" ItemsSource="{Binding Members}">
<StackPanel Orientation="Horizontal">
<Image Source="/WpfTutorialSamples;component/Images/group.png" Margin="0,0,5,0" />
<TextBlock Text="{Binding Name}" />
<TextBlock Text=" [" Foreground="Blue" />
<TextBlock Text="{Binding Members.Count}" Foreground="Blue" />
<TextBlock Text="]" Foreground="Blue" />
</StackPanel>
</HierarchicalDataTemplate>
<DataTemplate DataType="{x:Type self:FamilyMember}">
<StackPanel Orientation="Horizontal">
<Image Source="/WpfTutorialSamples;component/Images/user.png" Margin="0,0,5,0" />
<TextBlock Text="{Binding Name}" />
<TextBlock Text=" (" Foreground="Green" />
<TextBlock Text="{Binding Age}" Foreground="Green" />
<TextBlock Text=" years)" Foreground="Green" />
</StackPanel>
</DataTemplate>
</TreeView.Resources>
</TreeView>
If you try that code in the last link, notice when you click a node - a family or member - you get a blue background.
This is how you mark a selected treeviewitem.
Don't mess up your templates and it "just works".
If you need to know which one was selected then this is a bit tricky because you can't just bind selecteditem like a datagrid. This is indirectly due to the hierarchical nature of the control meaning each node has it's own itemcontrol for children. A treeviewitem is a headereditemscontrol.
This answer illustrates one of the behaviors commonly used to enable binding of selecteditem:
Data binding to SelectedItem in a WPF Treeview
Once you bind the selected item like this you can then work with that property in the viewmodel. When it changes the setter will be hit and you can put code in there to do work with that item.
I'm currently creating a menu with a ListView and ListViewItems. In the menu, I want to go back from one page to a predefined page. The ListviewItem, which were clicked, should disappear when it takes me back to the predefined page. However, I'm stuck at how the flow between the views is handled and how to hide the ListView when it is clicked.
XAML
<ListView x:Name="GoBackMenu" Foreground="#FF5C99D6" ScrollViewer.HorizontalScrollBarVisibility="Disabled" SelectionChanged="ListViewMenu_ClashPressed" SelectedIndex="{Binding SelectedIndex}" Margin="0 0 0 290" Visibility="visible" >
<ListViewItem x:Name="Swift_goBackView" Height="80">
<StackPanel Orientation="Horizontal">
<materialDesign:PackIcon Kind="Backburger" Height="25" Width="25" Margin="10"/>
<TextBlock Text="Back to overview" VerticalAlignment="Center" Margin="20 10"/>
</StackPanel>
</ListViewItem>
<ListView.ItemContainerStyle>
<Style TargetType="ListViewItem">
<EventSetter Event="PreviewMouseLeftButtonDown" Handler="ListViewItem_PreviewMouseLeftButtonDown" ></EventSetter>
</Style>
</ListView.ItemContainerStyle></ListView>
Thanks for the help in advance.
You could remove the clicked item like this in your event handler:
private void ListViewItem_PreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
ListViewItem lvi = (ListViewItem)sender;
GoBackMenu.Items.Remove(lvi);
}
How the "the flow between the views is handled" depends on your current navigation implementation which you haven't described.
I have a listbox with an Add button mapped to a command backed by a SelectedItem property in the VM.
When an item is added to the listbox i have the SelectedItem set to the new item so it has focus in the listbox. I'd like to have a textbox (data entry for that new item) to have focus.
I've been looking at event triggers but i havent seen a way to cross items but basically I think what i want is an event trigger for the listbox selection change to to set the focus on a text box.
how would i go about doing this?
As an example I have the following XAML code. This will add a Person (name and age property only)
Basically I want the txtName textbox to have focus when an item is selected in the listbox.
<StackPanel>
<TextBlock>Name</TextBlock>
<TextBox Text="{Binding NewPerson}"></TextBox>
<Button Command="{Binding AddPersonDelegateCommand}">Add</Button>
<Button>Remove</Button>
<ListBox ItemsSource="{Binding People}" DisplayMemberPath="Name"
SelectedItem="{Binding SelectedPerson}">
</ListBox>
<TextBox Name="txtName" Text="{Binding SelectedPerson.Name, Mode=TwoWay}"</TextBox>
<TextBox Name="txtAge" Text="{Binding SelectedPerson.Age, Mode=TwoWay}"></TextBox>
</StackPanel>
here is the xaml based trigger to set focus on the txtName TextBox when SelectedItem property is toggled from null
so idea here is that you set SelectedPerson property to null followed by the instance of newly created person object, that should do the trick and will set the focus to the desired TextBox
the limitation of this trigger is that you need to set SelectedPerson property null before you set to the new object, an attached behavior can solve that issue too, if this is not workable for you.
<StackPanel>
<TextBlock>Name</TextBlock>
<TextBox Text="{Binding NewPerson}"></TextBox>
<Button Command="{Binding AddPersonDelegateCommand}">Add</Button>
<Button>Remove</Button>
<ListBox DisplayMemberPath="Name"
x:Name="list"
SelectedItem="{Binding SelectedPerson}">
</ListBox>
<TextBox Name="txtName"
Text="{Binding SelectedPerson.Name, Mode=TwoWay}">
<TextBox.Style>
<Style TargetType="TextBox">
<Setter Property="FocusManager.FocusedElement"
Value="{Binding RelativeSource={RelativeSource Self}}" />
<Style.Triggers>
<DataTrigger Binding="{Binding SelectedItem,ElementName=list}"
Value="{x:Null}">
<Setter Property="FocusManager.FocusedElement"
Value="{x:Null}" />
</DataTrigger>
</Style.Triggers>
</Style>
</TextBox.Style>
</TextBox>
<TextBox Name="txtAge"
Text="{Binding SelectedPerson.Age, Mode=TwoWay}"></TextBox>
</StackPanel>
I'm writing an application wherein I would like to disable few items in the ComboBox and also want to disallow/block selection of disabled items. Please note ComboBox in main window has another ComboBox as ComboBox Item init (that is decided at run time by DataTemplateSelector).
With below code I'm able to disable a ComboBox within ComboBox but it would not stop user from selecting that disabled ComboBox item. Any help in disallow/block selection of disabled items would be helpful.
Below are the code snippets
ComboBox in main window:
<Grid>
<ComboBox HorizontalAlignment="Left" VerticalAlignment="Top"
Width="120" Margin="87.2,44.8,0,0"
ItemsSource="{Binding Cars}"
ItemsPanel="{DynamicResource ItemsPanelTemplateHorizontal}"
ItemTemplateSelector="{StaticResource QualityComboBoxTemplateSelector}"
SelectedItem="{Binding SelectedItm}"/>
</Grid>
Data template selector:
public class QualityComboBoxTemplateSelector : DataTemplateSelector
{
public override DataTemplate SelectTemplate(object item, DependencyObject container)
{
var element = container as FrameworkElement;
var dataTemplate = element.FindResource(((item is string) && item.Equals("Ferrari")) ?
"DataTemplateTopLevelCombobox2" : "DataTemplateTopLevelCombobox1") as DataTemplate;
return dataTemplate;
}
}
Data templates for above ComboBox:
<DataTemplate x:Key="DataTemplateTopLevelCombobox1">
<Border BorderBrush="Black" BorderThickness="1" >
<TextBlock HorizontalAlignment="Left"
TextWrapping="Wrap" Text="{Binding}"
VerticalAlignment="Top"/>
</Border>
</DataTemplate>
<DataTemplate x:Key="DataTemplateTopLevelCombobox2">
<Border Width="100">
<ComboBox Text="Custom" Height="21.96"
ItemsSource="{Binding DataContext.Models, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}}}"
IsEnabled="{Binding DataContext.EnableCombo, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}}}" />
</Border>
</DataTemplate>
You can achieve this by setting IsEnabled property of a ComboBoxItem to false;
So each item in ComboBox's ItemSource (i.e. Cars in your case) can be an object having some property (say IsSelectable) specifying whether it should be enabled or disabled and then use it with a style to make an item un-selectable. something like this -
<Style TargetType="ComboBoxItem">
<Setter Property="IsEnabled" Value="{Binding IsSelectable}"/>
</Style>
Update:
<Grid>
<ComboBox
Width="120"
Margin="87.2,44.8,0,0"
HorizontalAlignment="Left"
VerticalAlignment="Top"
ItemTemplateSelector="{StaticResource QualityComboBoxTemplateSelector}"
ItemsPanel="{DynamicResource ItemsPanelTemplateHorizontal}"
ItemsSource="{Binding Cars}"
SelectedItem="{Binding SelectedItm}">
<ComboBox.ItemContainerStyle>
<Style TargetType="ComboBoxItem">
<Setter
Property="IsEnabled"
Value="{Binding IsSelectable}" />
</Style>
</ComboBox.ItemContainerStyle>
</ComboBox>
</Grid>
To solve the problem pointed by #JordyBoom.
ItemsContainerGenerator does not generate items until dropdown is opened at least once.
So if you open the drop down and close it again in window’s loaded event handler then all supposed to work fine with mouse as well as with keyboard selection.
public MainWindow()
{
InitializeComponent();
this.Loaded += new RoutedEventHandler(onLoaded);
}
private void onLoaded(object sender, RoutedEventArgs e)
{
cmbx.IsDropDownOpen = true;
cmbx.IsDropDownOpen = false;
}
source: WPF: Making combo box items disabled – also when accessed using the keyboard
I have simple listbox here: link to image where I have binded data. Now when I tap on one item(one row) listbox item/row is selected/violet color but in fact this item is not real selected only row change color but when I click on image or text then row where I click in not selected/violin but item in code is selected. I'm not sure if You understand what I'm saying. In short if I click on blank space row then row is visual selected but sender like listbox item not get data, if I click on text or image then sender get data but its not visual selected. How I can do it when I click anywhere row is selected/violet and item-sender get data?
And my second question is why my rectangle used like line has max width like text I want strech this rectangle at full width of listbox is this possible?
XAML:
<ListBox x:Name="lbMessagesUsersList" Foreground="Black" ItemsSource="{Binding MyDatasMessagesUserList }">
<ListBox.ItemTemplate>
<DataTemplate>
<ListBoxItem Tapped="userTapped" Tag="{Binding}" >
<StackPanel>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="50"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Image Grid.Column="0" Source="{Binding MessengeHisPhoto}" HorizontalAlignment="Left" Width="40" Height="40" Margin="5,-18,0,-18" Stretch="Fill" ></Image>
<TextBlock x:Name="tbMessengerName" Text="{Binding MessengeFullName}" HorizontalAlignment="Left"
Grid.Column="1" Margin="25,0,0,0"/>
</Grid>
<Rectangle Height="1" Margin="0,0,0,-38" HorizontalAlignment="Stretch">
<Rectangle.Fill>
<SolidColorBrush Color="Black"/>
</Rectangle.Fill>
</Rectangle>
</StackPanel>
</ListBoxItem>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
CS:
private void userTapped(object sender, TappedRoutedEventArgs e)
{
var button = sender as ListBoxItem;
if (button != null)
{
var subject = MyDatasMessagesUserList.FirstOrDefault(sub => sub == button.Tag);
if (subject != null)
{
IdOfChoosenUser = subject.MessengeIdUser;
}
}...
I also try remove ListbOxItem and set binding tag to stackPanel but this dont work too.{
Note that, when you use an ItemTemplate, each item gets wrapped in a ListBoxItem -- so, you shouldn't use a ListBoxItem in the template, as that will produce two nested ones. Try removing it, like so:
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Tapped="userTapped" Tag="{Binding}">
<!-- content here -->
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
For the handler, it might be easier to simply reference the ListBox by name instead of trying to use Tag (thanks #MetroSmurf):
private void userTapped(object sender, TappedRoutedEventArgs e)
{
var selectedItem = lbMessagesUsersList.SelectedItem;
var subject = MyDatasMessagesUserList.FirstOrDefault(sub => sub == selectedItem);
}
For the second part of your question: to stretch the items, you need to stretch the ListBoxItem wrapper. Do this by using ItemContainerStyle:
<ListBox>
<ListBox.ItemContainerStyle>
<Style TargetType="ListBoxItem">
<Setter Property="HorizontalAlignment" Value="Stretch" />
<Setter Property="HorizontalContentAlignment" Value="Stretch" />
</Style>
</ListBox.ItemContainerStyle>
</ListBox>