WPF - Making selected listview column editable on button click - wpf

I have a listView with a gridview presentation and TextBlocks in each column. I would like to make the selected line editable by replacing the textblocks with TextBoxes and ComboBoxes when the user clicks on an edit button. I tried to do this by setting styles that toggles the visibility of the controls like this :
<Style x:Name="ItemDisplayStyle" TargetType="{x:Type TextBlock}" x:Key="ItemDisplayStyle">
<Setter Property="VerticalAlignment" Value="Center" />
<Setter Property="Visibility" Value="{Binding Path=dislayMode}" />
</Style>
<Style x:Name="ItemEditStyle" TargetType="{x:Type FrameworkElement}" x:Key="ItemEditStyle">
<Setter Property="VerticalAlignment" Value="Center" />
<Setter Property="Visibility" Value="{Binding Path=editMode}" />
</Style>
displayMode and editMode are Visibility properties set in the code-behind.
And lower in the xaml :
<GridViewColumn Header="Date de début" Width="80">
<GridViewColumn.CellTemplate>
<DataTemplate>
<Grid>
<TextBlock Margin="-6,0"
HorizontalAlignment="Stretch" TextAlignment="Center"
Text="{Binding Path=DateDebut, Mode=TwoWay}"
Style="{StaticResource ItemDisplayStyle}" />
<TextBox x:Name="tbDateDebut" Margin="-6,0"
HorizontalAlignment="Stretch" HorizontalContentAlignment="Center"
Text="{Binding Path=DateDebut, Mode=TwoWay}"
Style="{StaticResource ItemEditStyle}" />
</Grid>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
My problem is that changing 'editMode' and 'displayMode' in the code-behind does not seem to be detected at the UI level.
Also, even if I get this to work, I don't have any idea of how to apply it only to the selected line.
I can do this separately by binding the visibility value with the ListView so that when the user selects a line, she/he gets the editable controls on that line but I really want to allow this only when they click on a button.

Have you Refreshed the contents of the Grid after making changes? You can use the Grid.GetColumn method and send the sender object i.e. edit button (which I suppose would be separate for each column) and then probably use the VisualTreeHelper to get the textbox and combobox in that column.
Also, why don't you use the 'IsReadOnly' property of the TextBox instead of replacing the TextBlock? Make it true or false as per your requirement.

Related

Excess border selection in WPF's Lisbox [duplicate]

I have a ListBox in which each item is a StackPanel. The StackPanel consist of an Image and a TextBlock below it:
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Margin="10">
<Image>
<Image.Source>
<BitmapImage UriSource="{Binding Path=ImageFilePath}"/>
</Image.Source>
</Image>
<TextBlock Text="Title" TextAlignment="Center"/>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
It looks like this:
When the user select an item, I get the default blue rectangle that surround the StackPanel:
Now, I want to make a different border for the selected-item, but I want it to surround only the image.
I know how to make a control template and put a custom border around the ContentPresenter, but this, of course, will surround the whole StackPanel, not only the Image.
I don’t know if making changes to the ContentPresenter is possible, and if it is a good idea at all. If there is other way to achieve the look I want, it will be fine as well.
Right, the ListBox's own ContentPresenter isn't helpful for what you're doing. You want to a) eliminate the ListBox's own selection visuals and b) replace them with something more suitable in the DataTemplate for your items.
The default selection visual is applied by the default template for ListBoxItem. So replace that template. Using a Style in the resources for your ListBox, apply your own control template to ListBoxItem. Not much to it, just present the content and don't provide a selection background. Then you handle the selection visuals with a trigger in your data template, where your image and your label are defined and you can apply changes to one and not the other. The below example works for me.
Note that there's some fiddling with the HorizontalAlignment on the Border element to make it cling to the Image element within it. Also, I wrote a quickie test viewmodel whose Items property is called Items; I assume this is not the name of the collection member you're using to populate your own ListBox.
<ListBox
Margin="8"
ItemsSource="{Binding Items}"
>
<ListBox.Resources>
<Style TargetType="{x:Type ListBoxItem}">
<Setter Property="HorizontalContentAlignment" Value="Stretch" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ListBoxItem}">
<Grid>
<ContentPresenter />
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ListBox.Resources>
<ListBox.ItemTemplate>
<DataTemplate>
<Grid>
<Border
x:Name="HighlightBorder"
BorderThickness="4"
HorizontalAlignment="Left"
VerticalAlignment="Top"
Margin="10"
>
<Border.Style>
<Style TargetType="Border">
<!-- MUST set default BorderBrush via a style, if you set it at all.
As an attribute on the Border tag, it would override the effects of
the trigger below.
-->
<Setter Property="BorderBrush" Value="Transparent" />
</Style>
</Border.Style>
<Image Source="{Binding ImageFilePath}" />
</Border>
</Grid>
<DataTemplate.Triggers>
<DataTrigger
Binding="{Binding IsSelected, RelativeSource={RelativeSource AncestorType=ListBoxItem}}"
Value="True">
<Setter TargetName="HighlightBorder" Property="BorderBrush" Value="Orange" />
</DataTrigger>
</DataTemplate.Triggers>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>

WPF - Highlight Textbox Based on Content

I'm learning WPF/MVVM and I have a combo box that updates several text boxes based on the selected combo box value. What I would like to do is highlight some of the thext boxes if they contain a certain value. The text boxes do not receive focus and are read only. What is the best way to go about something like this using MVVM?
EDIT:
Thank you for the idea of using a Trigger. I'm actually trying to set the border color of the text box based on the value of the same text box. Based on the information provided below, this is what my text box looks like, but when the word "Transaction" shows up in the textbox, the border doesn't change. Am I referencing something incorrectly?
<TextBox Height="23" HorizontalAlignment="Left" VerticalAlignment="Top" Width="170" Grid.Column="4" Grid.Row="2" Margin="4,2,0,0" IsEnabled="False" DataContext="{Binding SelectedTDetails}" Text="{Binding CType}" Background="WhiteSmoke" Padding="1,0,0,0" >
<TextBox.Style>
<Style TargetType="TextBox">
<Style.Triggers>
<Trigger Property="Text" Value="Transaction">
<Setter Property="BorderBrush" Value="LightGreen" />
</Trigger>
</Style.Triggers>
</Style>
</TextBox.Style>
</TextBox>
here is a sample using Triggers in TextBox
while using this sample trigger the same by typing item 2 in TextBox or select it from ComboBox
<StackPanel>
<ComboBox x:Name="combo">
<ComboBoxItem>item 1</ComboBoxItem>
<ComboBoxItem>item 2</ComboBoxItem>
</ComboBox>
<TextBox Text="{Binding SelectedItem.Content,ElementName=combo}">
<TextBox.Style>
<Style TargetType="TextBox">
<Style.Triggers>
<Trigger Property="Text"
Value="item 2">
<Setter Property="Background"
Value="LightGreen" />
</Trigger>
</Style.Triggers>
</Style>
</TextBox.Style>
</TextBox>
</StackPanel>
in above example the TextBox is bound to ComboBox's Selected Item (just to simulate your app's behavior)
The Trigger in TextBox's style look if the Text property contains item 2 and it changes the Background color of TextBox to simulate the highlight
This is just a basic idea on triggers, you may use your creativity to implement the desired highlight behavior perhaps involving flashing color, animated sizing etc.

Change DockPanel's background to match the disabled ListView's background color

I have a ListView inside a DockPanel. I need the ListView to be stretched horizontally and centered vertically inside the DockPanel. When the ListView is disabled (IsEnabled=false) I need to change the DockPanel's background to match the disabled ListView's background color.
So, basically I want to avoid the situation that can be seen on the picture below.
<Window>
<DockPanel IsEnabled="False">
<ListView IsEnabled="False" VerticalAlignment="Center" HorizontalContentAlignment="Center">
<ListViewItem Content="AAA"/>
<ListViewItem Content="BBB"/>
<ListViewItem Content="CCC"/>
</ListView>
</DockPanel>
</Window>
However, I don't want to explicitly declare colors anywhere in my code, because I don't know what kind of background color will a ListView use in different Windows environments (I'm not sure, but I guess different Windows theme/color settings can alter the default background color of the ListView - which potentially could be different from the color that I don't want to explicitly declare).
Binding the ListView's background color to the DockPanel's background color doesn't work.
So, right now I'm using the following workaround.
<DockPanel.Style>
<Style TargetType="{x:Type DockPanel}">
<Style.Triggers>
<DataTrigger Binding="{Binding ElementName=parametersListView, Path=IsEnabled}" Value="False">
<Setter Property="Background" Value="{DynamicResource {x:Static SystemColors.ControlBrushKey}}"/>
</DataTrigger>
</Style.Triggers>
</Style>
</DockPanel.Style>
I've extracted the resource key that my ListView's using in its ControlTemplate. On the template there is a Trigger declared on the IsEnabled property and it sets the control's background color to the color represented by the SystemColors.ControlBrushKey key.
This seems to work, but I'm not sure if this is the right way to do this.
Is there a way to do this in a more robust fashion, or this is the best that I could do?
(On a side note: using SystemColors.ControlBrush instead of SystemColors.ControlBrushKey produces a different shade of grey, I'm not sure why.)
Why not use LstFilledChild =true
Edit:
If you name your element and use the ElementName in the binding it should work.
<DockPanel HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Background="{Binding Background, ElementName=myList}">
<ListView Name="myList" IsEnabled="False" HorizontalAlignment="Center" HorizontalContentAlignment="Center" >
<ListViewItem>AAA</ListViewItem>
</ListView>
</DockPanel>
Since I've asked my question the application I was working on is live and it's using the solution I've originally come up which is the following. It works well for the moment.
<Window>
<DockPanel IsEnabled="False">
<DockPanel.Style>
<Style TargetType="{x:Type DockPanel}">
<Style.Triggers>
<DataTrigger Binding="{Binding ElementName=myListView, Path=IsEnabled}"
Value="False">
<Setter Property="Background"
Value="{DynamicResource {x:Static SystemColors.ControlBrushKey}}"/>
</DataTrigger>
</Style.Triggers>
</Style>
</DockPanel.Style>
<ListView IsEnabled="False"
VerticalAlignment="Center"
HorizontalContentAlignment="Center"
x:Name="myListView"
x:FieldModifier="private">
<ListViewItem Content="AAA"/>
<ListViewItem Content="BBB"/>
<ListViewItem Content="CCC"/>
</ListView>
</DockPanel>
</Window>

Listbox doesn't detect selected item

I have the following listbox:
<ListBox ItemsSource="{Binding AvailableTemplates}" Style="{DynamicResource SearchListBoxStyle}" SelectedItem="{Binding SelectedTemplate, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}">
<ListBox.ItemTemplate>
<DataTemplate>
<RadioButton Content="{Binding}" GroupName="group" />
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
this doesn't detect as selected item changed if i select on the radiobutton. It only detects if i click under the radio button on the listbox row. Any ideeas how to amend to detect as selected item changed when clicking on the radio button?
If you only want to synchronize the RadioButton.IsChecked with ListBoxItem.IsSelected, you can use a binding
<RadioButton Content="{Binding}" GroupName="group"
IsChecked="{Binding Path=IsSelected, RelativeSource={
RelativeSource AncestorType={x:Type ListBoxItem}},Mode=TwoWay}"/>
If you don't want your items synchronized, you can use a Trigger that sets IsSelected whenever the item gets keyboard focus, although this will only keep an item selected as long as it has keyboard focus
<Style TargetType="ListBoxItem">
<Style.Triggers>
<Trigger Property="IsKeyboardFocusWithin" Value="True">
<Setter Property="IsSelected" Value="True" />
</Trigger>
</Style.Triggers>
</Style>
And if you want it selected regardless of if the element still has keyboard focus or not, you have to use a little code behind
<Style TargetType="{x:Type ListBoxItem}">
<EventSetter Event="PreviewGotKeyboardFocus" Handler="SelectCurrentItem"/>
</Style>
protected void SelectCurrentItem(object sender, KeyboardFocusChangedEventArgs e)
{
ListBoxItem item = (ListBoxItem)sender;
item.IsSelected = true;
}
You can not mistake: listBox.SelectedItem and radioButton.IsChecked
They are totally different things,
SelectedItem is called the ListBoxItem, your radiobutton is within a listboxitem.
You must make a binding for property IsChecked (RadioButton).
Try setting ListBoxItem.IsHitTestVisible to false (you may do this in xaml). It solved my selection problem elementary. My problem was that selection only worked when I clicked on white space in ListBox line but not a custom content.

WPF DataGrid Hyperlink Appearance and Behaviour

I am quite new to WPF, there is so much to learn and I think I'm getting there, slowly. I have a DataGrid which is used for display and user input, it's quite a tricky Grid as it is the main focus of the entire application. I have some columns that are read-only and I have used a CellStyle Setter to set KeyboardNavigation.IsTabStop to False to keep user input focused on the important columns and that works fine. I would like a couple of the read-only columns to be hyperlinks that show a tooltip and do not receive the focus, however I am struggling to write the XAML that will achieve all three requirements at the same time.
One of the columns is to indicate if the item on the row has any Notes. I have used the following XAML to display a HasNotes property in the cell in a DataGridTemplateColumn and on the Tooltip show the actual notes, in the Notes property:
<DataGridTemplateColumn x:Name="NotesColumn" Header="Notes">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBlock Text="{Binding HasNotes, Mode=OneWay}">
<TextBlock.ToolTip>
<TextBlock Text="{Binding Notes}" MaxWidth="300" TextWrapping="Wrap" />
</TextBlock.ToolTip>
</TextBlock>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
<DataGridTemplateColumn.CellStyle>
<Style>
<Setter Property="KeyboardNavigation.IsTabStop" Value="False"/>
</Style>
</DataGridTemplateColumn.CellStyle>
</DataGridTemplateColumn>
That works fine but I would like to make it a Hyperlink instead so the user can do something with the Notes when they click on the cell contents.
I have another column which is a DataGridHyperlinkColumn used to display a Unit of Measurement on a hyperlink and when clicked the user can change the unit. (The reason I have done it like this, rather than a ComboBox for example, is because as much as I want the user to be able to change the unit I want to make the interface such that it is a very deliberate act to change the unit, not something that can be done accidentally). The following XAML puts the Hyperlink column on for Unit
<DataGridHyperlinkColumn x:Name="ResultUnitLink" Binding="{Binding Path=Unit.Code}" Header="Unit" Width="Auto" IsReadOnly="True">
<DataGridHyperlinkColumn.CellStyle>
<Style>
<Setter Property="KeyboardNavigation.IsTabStop" Value="False"/>
</Style>
</DataGridHyperlinkColumn.CellStyle>
<DataGridHyperlinkColumn.ElementStyle>
<Style>
<EventSetter Event="Hyperlink.Click" Handler="ChangeUnit" />
</Style>
</DataGridHyperlinkColumn.ElementStyle>
</DataGridHyperlinkColumn>
One problem with the XAML for the Hyperlink column is that the IsTabStop = False doesn't appear to work, when tabbing through the Grid my hyperlink column still receives the focus, unlike the other columns where I've used a setter to change IsTabStop. If push comes to shove I could live with that but I'd rather not.
What I actually want from both those columns is an amalgamation of the two appearances/behaviours i.e. Columns where the data is displayed on a hyperlink, where TabStop = False and which display a tooltip of a different property when hovered over.
Can anyone help advise me how to get a column that achieves the following:
Hyperlink displaying one property
Tooltip displaying a different property
IsTabStop = False that actually works when used with a hyperlink
Thanks in advance to anyone who can help.
I've had issues with the Hyperlink in the past, so have this Style which I use for Labels or Buttons to make them look like Hyperlinks. Try making your column Template into a Button or a Label and applying this style.
<Style x:Key="ButtonAsLinkStyle" TargetType="{x:Type Button}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Button}">
<TextBlock HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
<ContentPresenter ContentStringFormat="{TemplateBinding ContentStringFormat}" />
</TextBlock>
</ControlTemplate>
</Setter.Value>
</Setter>
<Setter Property="Foreground" Value="Blue" />
<Setter Property="Cursor" Value="Hand" />
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Foreground" Value="Red" />
</Trigger>
</Style.Triggers>
</Style>

Resources