WPF '_' character in Property-Binding does not work - wpf

On the MainWindow of my WPF application I have a simple listBox that is databound to an ObservableCollection. The ObservableCollection contains members of a simple "Product" class with one string-property. The goal is to show the text that is stored in the "PName" property of all Products of the ObservableCollection.
The MainWindow.xaml:
<Window x:Class="BindingCheck.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:BindingCheck"
mc:Ignorable="d"
Title="MainWindow" Height="350" Width="525">
<Grid>
<ListBox Name="listBox1" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" ItemsSource="{Binding}">
<ListBox.ItemTemplate>
<DataTemplate>
<Label FontSize="26" Content="{Binding Path=PName}"/>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Grid>
The MainWindow.Xaml.Cs:
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Windows;
namespace BindingCheck
{
public class Product
{
private string pName;
public string PName
{
get { return pName; }
}
public Product(string pname)
{
pName = pname;
}
}
public partial class MainWindow : Window
{
private ObservableCollection<Product> products;
public MainWindow()
{
InitializeComponent();
products = new ObservableCollection<Product>();
products.Add(new Product("Toaster"));
products.Add(new Product("Big_Toaster"));
products.Add(new Product("Very_Big_Toaster"));
this.DataContext = products;
}
}
}
And now my question: Why is only every second '_' character shown in the listBox output? The Output-Items should be: "Toaster", "Big_Toaster" and "Very_Big_Toaster" but however I get another output:
Output-Items in listBox: Toaster, BigToaster, VeryBig_Toaster

See below code:
<StackPanel>
<Label Content="_first_second" Target="{Binding ElementName=txtbox}"/>
<TextBox Name="txtbox" Width="100" Height="50"/>
</StackPanel>
Now after the screen is Loaded press key 'f' any you'll see the Focus is set in the TextBox.
Cause the the content of Label is treated as AccessText for Label
here and according to AccessText definition first '' char means
the Accesskey to element and '' is not visible.
use below links for ref:
MSDN on AccessText
AccessText example

Related

It is show the name of the class when I use a template in a combobox

I want to update the text of an item in a combobox when the item is saved.
Although the entity that I use as source implements the notify property changed event, it is not update. I have read the solution it is to use a data templeate instead of DisplayMemberPath.
So I am using this:
<DataTemplate x:Key="TipoComponenteTemplate"
DataType="{x:Type clases:TiposComponentesUI}">
<TextBlock Text="{Binding TipoComponente}"/>
</DataTemplate>
<ComboBox Name="cmbTiposComponentes" Width="150"
Text="{Binding TiposComponentesTexto, UpdateSourceTrigger=PropertyChanged}"
ItemsSource="{Binding TiposComponentes}"
ItemTemplate="{StaticResource ResourceKey=TipoComponenteTemplate}"
SelectedItem="{Binding TiposComponentesSelectedItem}">
That works partially, in the way when I open the combobox, the items show the correct text, but when I select one of them, in the textbox of the combobox it is shown the name of the class instead of the data in the property TipoComponente that is defined in the data template.
So I would like to know how I could show the same text in the textbox than the text that is shown in the items.
Thanks.
I've reproduced this scenario and both the the ItemTemplate and the DisplayMemberPath solutions work fine for me, as long as the ComboBox's IsEditable property is set to False.
When IsEditable="False"
Here's what I have:
Book.cs
using CommunityToolkit.Mvvm.ComponentModel;
namespace WpfApp2;
public partial class Book : ObservableObject
{
[ObservableProperty]
private string? _author;
[ObservableProperty]
private string? _title;
}
MainViewModel.cs
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
using System;
using System.Collections.ObjectModel;
namespace WpfApp2;
public partial class MainViewModel : ObservableObject
{
public ObservableCollection<Book> Books { get; } = new();
[ObservableProperty]
private Book? _selectedBook;
public MainViewModel()
{
Books.Add(new Book { Author = "Jim Weasley", Title = "The mystery of eggplants" });
Books.Add(new Book { Author = "Ryan Reynolds", Title = "Doplhins and how to beat them" });
Books.Add(new Book { Author = "Sarah O'Connel", Title = "What is djent?" });
}
[RelayCommand]
private void UpdateRandomBookTitles()
{
foreach (var book in Books)
{
book.Title = Guid.NewGuid().ToString();
}
}
}
MainWindow.xaml.cs
<Window
x:Class="WpfApp2.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:WpfApp2"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
Title="MainWindow"
Width="800"
Height="450"
mc:Ignorable="d">
<Window.DataContext>
<local:MainViewModel />
</Window.DataContext>
<Window.Resources>
<DataTemplate x:Key="BookDisplayTemplate" DataType="{x:Type local:Book}">
<TextBlock Text="{Binding Title}" />
</DataTemplate>
</Window.Resources>
<StackPanel>
<Button Command="{Binding UpdateRandomBookTitlesCommand}" Content="Randomize book titles" />
<ComboBox
ItemTemplate="{StaticResource BookDisplayTemplate}"
ItemsSource="{Binding Books}"
SelectedItem="{Binding SelectedBook}" />
<!-- This will work as well -->
<!-- <ComboBox
DisplayMemberPath="Title"
ItemsSource="{Binding Books}"
SelectedItem="{Binding SelectedBook}" />-->
</StackPanel>
</Window>
After you've selected a book, changing it's Title will be reflected in ComboBox's Text as well.
The problem: when IsEditable="True"
Once you set IsEditable="True", this stops working and I believe it's intentional. When IsEditable="True", the ComboBox's Text is only update when:
an item is selected
user types text manually
Changing the SelectedBook's Title does nothing here, my guess is so that it doesn't overwrite user input. Only selected another item will do that.

Trying to convert a Winforms data source to a WPF bindable (ObservableCollection) type

I'm trying to convert a Winforms app to a WPF app and have this code:
ObservableCollection<Dictionary<string, string>> PlatypusDict =
new ObservableCollection<Dictionary<string, string>>();
. . .
PlatypusDict = PlatypusData.getPlatypusAccountsForCentury(Convert.ToInt32(labelPlatypusName.Tag));
...which gives me this err msg:
Cannot implicitly convert type 'System.Collections.Generic.Dictionary' to 'System.Collections.ObjectModel.ObservableCollection>'
...and this doesn't help any:
PlatypusDict = (System.Collections.ObjectModel.ObservableCollection<System.Collections.Generic.Dictionary<string, string>>)PlatypusData.getPlatypusAccountsForCentury(Convert.ToInt32(labelPlatypusName.Tag));
Do I not really need to use an ObservableCollection to bind my ListView to, or what is the workaround/proper methodology here?
You can just binding Dictionary to ListView.
Example:
XAML file:
<Window x:Class="BindingDictionaryLB.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">
<Grid>
<ListView ItemsSource="{Binding}">
<ListView.ItemTemplate>
<DataTemplate>
<StackPanel>
<TextBlock Margin="2" Text="{Binding Key}" />
<TextBlock Margin="2" Text="{Binding Value}" />
</StackPanel>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</Grid>
</Window>
Code-behind file:
using System.Collections.Generic;
using System.Windows;
namespace BindingDictionaryLB
{
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
Dictionary<string, string> _source = new Dictionary<string, string>();
for (int i = 0; i < 5; i++)
{
_source.Add("key_" + i, "value_" + i);
}
this.DataContext = _source;
}
}
}

Trying to bind TextBlock which is inside the DataTemplate of an accordion control in Silverlight. Text is not visible when I run the application

I amsomewhat new to Silverlight, What I want to do is to show the Title in the accordion control which is bound from the property of that user control. I have a TextBlock which is inside the DataTemplate of an Accordion control in Silverlight. When I run the application, text is coming blank and nothing is displayed in the Accordion title.
<UserControl x:Class="SilverlightApplication1.SilverlightControl1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:layoutToolkit="clr- namespace:System.Windows.Controls;assembly=System.Windows.Controls.Layout.Toolkit"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="400" DataContext="{Binding RelativeSource={RelativeSource Self}}">
<Grid x:Name="LayoutRoot" Background="White">
<layoutToolkit:Accordion x:Name="accordionFilter" HorizontalAlignment="Stretch" SelectionMode="ZeroOrMore">
<layoutToolkit:AccordionItem MinHeight="0" MaxHeight="120" IsSelected="True">
<layoutToolkit:AccordionItem.HeaderTemplate>
<DataTemplate>
<Grid Height="22" VerticalAlignment="Center">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="100*"></ColumnDefinition>
</Grid.ColumnDefinitions>
<TextBlock
Text="{Binding MainPageSelectedText}"
Width="150"></TextBlock>
</Grid>
</DataTemplate>
</layoutToolkit:AccordionItem.HeaderTemplate>
</layoutToolkit:AccordionItem>
</layoutToolkit:Accordion>
</Grid>
</UserControl>
using System;
using System.ComponentModel;
using System.Windows.Controls;
namespace SilverlightApplication1
{
public partial class SilverlightControl1 : UserControl, INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string name)
{
PropertyChangedEventHandler ph = this.PropertyChanged;
if (ph != null)
ph(this, new PropertyChangedEventArgs(name));
}
public SilverlightControl1()
{
InitializeComponent();
MainPageSelectedText = "Sample Text";
}
public string MainPageSelectedText
{
get { return _MainPageSelectedText; }
set
{
string myValue = value ?? String.Empty;
if (_MainPageSelectedText != myValue)
{
_MainPageSelectedText = value;
OnPropertyChanged("MainPageSelectedText");
}
}
}
private string _MainPageSelectedText;
}
}
On your data template level you don't have direct access to the user control's DataContext so your binding should look sth like:
Text="{Binding MainPageSelectedText, ElementName=MyUserControl}"
assuming that you will set Name/x:Name of your user control to MyUserControl.

WPF - CollectionViewSource Filter event in a DataTemplate not working

I'm seeing some really weird behavior where WPF isn't doing what I expect it to do. I've managed to boil the problem down the following bit of code:
XAML:
<Window x:Class="WpfApplication3.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">
<TabControl x:Name="tabControl">
<TabControl.ContentTemplate>
<DataTemplate DataType="{x:Type List}">
<UserControl>
<UserControl.Resources>
<CollectionViewSource x:Key="filteredValues" Source="{Binding}" Filter="CollectionViewSource_Filter" />
</UserControl.Resources>
<ListBox ItemsSource="{Binding Source={StaticResource filteredValues}}" />
</UserControl>
</DataTemplate>
</TabControl.ContentTemplate>
</TabControl>
</Window>
Code-behind:
using System.Collections.Generic;
using System.Windows;
using System.Windows.Data;
namespace WpfApplication3
{
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
this.tabControl.ItemsSource = new List<List<string>>()
{
new List<string>() { "a", "b", "c"},
};
}
private void CollectionViewSource_Filter(object sender, FilterEventArgs e)
{
string item = (string)e.Item;
e.Accepted = item.StartsWith("b");
}
}
}
I'd expect that this code would result in a TabControl with a single tab that has a ListBox with a single item that says "b." But, instead, I get a ListBox with all 3 of the strings. Setting a breakpoint inside CollectionViewSource_Filter shows that the filter never even runs.
What's going on here? Why isn't the filter working?
I was thinking maybe it has something to do with the CollectionViewSource being a resource in a DataTemplate. The events on the ListBox fire properly. If the UserControl is not part of a DataTemplate, the Filter event works fine.
EDIT:
For example, the following works as expected, with the List being filtered as expected.
XAML:
<Window x:Class="WpfApplication3.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">
<UserControl>
<UserControl.Resources>
<CollectionViewSource x:Key="filteredValues" Source="{Binding}" Filter="CollectionViewSource_Filter" />
</UserControl.Resources>
<ListBox ItemsSource="{Binding Source={StaticResource filteredValues}}" />
</UserControl>
</Window>
Code behind:
using System.Collections.Generic;
using System.Windows;
using System.Windows.Data;
namespace WpfApplication3
{
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
this.DataContext = new List<string>() { "a", "b", "c" };
}
private void CollectionViewSource_Filter(object sender, FilterEventArgs e)
{
string item = (string)e.Item;
e.Accepted = item.StartsWith("b");
}
}
}
Well, I don't know why it doesn't work, but at this point, I'm assuming it's a Microsoft bug. I'll probably be filing a Connect report shortly.
To work around the bug, I did the following. I created a subclass of CollectionViewSource like this:
using System.Windows.Data;
namespace WpfApplication3
{
internal class CustomFilteredCollectionViewSource : CollectionViewSource
{
public CustomFilteredCollectionViewSource()
: base()
{
this.Filter += CustomFilter;
}
private void CustomFilter(object sender, FilterEventArgs args)
{
string item = (string)args.Item;
args.Accepted = item.StartsWith("b");
}
}
}
I then replaced
<CollectionViewSource x:Key="filteredValues" Source="{Binding}" Filter="CollectionViewSource_Filter" />
with
<local:CustomFilteredCollectionViewSource x:Key="filteredValues" Source="{Binding}" />
and it now works perfectly.
You're using the Filter as if it's a property on the CollectionViewSource which always gets used.
It isn't. It's an event. It says, "When you filter this CollectionViewSource, this event will be called." It will respond to requests to filter, but won't trigger those requests itself.
I don't know a huge amount about CollectionViewSource, but I assume you'd have to bind it to a filtering control in order for this event to be triggered, like a Grid which allowed filtering.
I came across this same issue today and found a much easier workaround.
The issue is that the Filter event is not subscribed properly for some reason. You can get around this by subscribing to Filter in the Loaded event of the control which contains the CollectionViewSource as a resource.
Applying this to the example in the question you get
XAML:
<Window x:Class="WpfApplication3.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">
<TabControl x:Name="tabControl">
<TabControl.ContentTemplate>
<DataTemplate DataType="{x:Type List}">
<UserControl Loaded="UserControl_OnLoaded">
<UserControl.Resources>
<CollectionViewSource x:Key="filteredValues" Source="{Binding}"/>
</UserControl.Resources>
<ListBox ItemsSource="{Binding Source={StaticResource filteredValues}}" />
</UserControl>
</DataTemplate>
</TabControl.ContentTemplate>
</TabControl>
Code Behind:
using System.Collections.Generic;
using System.Windows;
using System.Windows.Data;
namespace WpfApplication3
{
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
this.tabControl.ItemsSource = new List<List<string>>()
{
new List<string>() { "a", "b", "c"},
};
}
private void CollectionViewSource_Filter(object sender, FilterEventArgs e)
{
string item = (string)e.Item;
e.Accepted = item.StartsWith("b");
}
private void UserControl_OnLoaded(object sender, RoutedEventArgs e)
{
var control = (UserControl) sender;
var cvs = (CollectionViewSource) control.Resources["filteredValues"];
cvs.Filter += CollectionViewSource_Filter;
}
}
}

Need simple example of WPF binding Objects to Listbox with LINQ

The following example successfully binds objects with a ListBox to display them.
However, I would like to create all the objects in one class and then from another class query them with LINQ to fill my XAML ListBox, what would I need to add this example:
XAML:
<Window x:Class="WpfApplication15.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Window1" Height="300" Width="300"
xmlns:local="clr-namespace:WpfApplication15">
<Window.Resources>
<ObjectDataProvider x:Key="customers" ObjectType="{x:Type local:Customers}"/>
<DataTemplate x:Key="LastNameFirst" DataType="WpfApplication15.Customer">
<StackPanel Margin="10 10 10 0" Orientation="Horizontal">
<TextBlock Text="{Binding Path=LastName}" FontWeight="bold"/>
<TextBlock Text=", "/>
<TextBlock Text="{Binding Path=FirstName}"/>
</StackPanel>
</DataTemplate>
</Window.Resources>
<Grid>
<ListBox ItemsSource="{Binding Source={StaticResource customers}}"
ItemTemplate="{StaticResource LastNameFirst}"/>
</Grid>
</Window>
Code behind:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
namespace WpfApplication15
{
public partial class Window1 : Window
{
public Window1()
{
InitializeComponent();
}
}
public class Customer
{
public string FirstName { get; set; }
public string LastName { get; set; }
public Customer(string firstName, string lastName)
{
this.FirstName = firstName;
this.LastName = lastName;
}
}
public class Customers : List<Customer>
{
public Customers()
{
this.Add(new Customer("Jim", "Thompson"));
this.Add(new Customer("Julie", "Watson"));
this.Add(new Customer("John", "Walton"));
}
}
}
edit: added ToList call to the LINQ query
You can just assign the ItemsSource of the ListBox using LINQ in code-behind for this. Assuming you give your ListBox a name:
<Window x:Class="WpfApplication15.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:local="clr-namespace:WpfApplication15"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Width="300" Height="300" Title="Window1">
<Window.Resources>
<DataTemplate x:Key="LastNameFirst" DataType="WpfApplication15.Customer">
<StackPanel Margin="10 10 10 0" Orientation="Horizontal">
<TextBlock FontWeight="bold" Text="{Binding Path=LastName}"/>
<TextBlock Text=", "/>
<TextBlock Text="{Binding Path=FirstName}"/>
</StackPanel>
</DataTemplate>
</Window.Resources>
<Grid>
<ListBox x:Name="lstCustomers"
ItemTemplate="{StaticResource LastNameFirst}"/>
</Grid>
</Window>
You can assign to ItemsSource in the Loaded event:
public partial class Window1 : Window
{
public Window1()
{
this.Loaded += new RoutedEventHandler(Window1_Loaded);
InitializeComponent();
}
void Window1_Loaded(object sender, RoutedEventArgs e)
{
Customers customers = new Customers();
lstCustomers.ItemsSource = customers.Where(customer => customer.LastName.StartsWith("W")).ToList();
}
}
Assuming your LINQ query will change depending on some logic, you can re-assign ItemsSource at the appropriate points.
If you want to bind without dipping into code-behind whenever your query logic changes, you're probably better off using a CollectionViewSource, since it has sorting and filtering capabilities (assuming that's what you're after from using LINQ).
Take a look # http://www.codeplex.com/bindablelinq and you should find a bunch of good examples for this.

Resources