Binding and support for rich text formatting - c#

Consider the following scenario:
I have a ListView that is bound to an ObservableCollection using the DataContext:
<ListView ItemsSource="{Binding}">
The class containing the string data uses the DependencyProperty mechanism to keep the displayed content synced with the data collection.
The ListView has one column that is editable (I followed the tutorial here to achieve this); the ListViewItem is then either a TextBlock or a TextBox. This is done using a DataTemplate and two Style resources.
I'd like to format the string displayed in the TextBlock based on a search string. Specifically, I'd like to format the items of the ListView to become bold as the user types in their search query if there is a match (only the characters that match in sequence should be made bold). This only needs to be displayed for the text currently being rendered using the TextBlock (that is, text not currently being edited).
I've considered using an IMultiValueConverter that takes in a reference to the TextBlock that renders the data so that I can format the text appropriately. However, this will destroy the binding that I've set up:
<TextBlock.Text>
<MultiBinding Converter="{StaticResource searchFormatter}" ConverterParameter="{x:Reference Name=txtSearch}">
<MultiBinding.Bindings>
<Binding Path="NameOfBoundDependencyProperty"/>
<Binding RelativeSource="{RelativeSource Self}"/>
</MultiBinding.Bindings>
</MultiBinding>
</TextBlock.Text>
searchFormatter being the IMultiValueConverter and txtSearch being the TextBox containing the search query.
I'm still learning WPF so I'm not familiar with the best approach or to what's possible. Is there a way to keep the data bound (so that edits reflect in the collection and the ListView) and still represent the data differently to the user (so that search matches may be bold)? Perhaps it would be cleaner if I manage the binding manually?

I decided to use a Control that supports HTML so that I could use an IValueConverter to update the value of the displayed text on the fly without affecting any active bindings. I used the code from here and modified it so that it looked like a TextBlock within my ListView:
BorderBrush = Brushes.Transparent;
SelectionBrush = Brushes.Transparent;
Cursor = Cursors.Arrow;
BorderThickness = new Thickness(0);
Background = Brushes.Transparent;
However, I still needed to trigger the IValueConverter so that the display is updated as the user types in their search query (code from here):
ICollectionView view = CollectionViewSource.GetDefaultView(ItemsSource);
view.Refresh();
I didn't want to slow down the search process so I only forced this refresh if there was actually a match (or if the state of having a match moves to no match). My IValueConvertor simply inserted the bold tags to match the search query:
<RichTextBox Text="{Binding Path=DisplayItem, Converter={StaticResource searchFormatter}, ConverterParameter={x:Reference txtSearch}}"/>
Where searchFormatter this time is an IValueConvertor.

Related

WPF set Binding dynamically back to original state after changing Text property of textbox in C#

I have a couple of textboxes which are set with complex Databinding in WPF.
This works fine when just selecting items from a listbox in viewsource. (using Observable collection)
However, I am having an issue when I press a certain button I want the textboxes to become blank or contain a zero. So I set the .Text in C# to 0 or blank depending on the textbox. However it seems by doing that it also removes my bindings on this element. How can I get my bindings back in the original state through C#?
Xaml example
<TextBox Name="BandNrTextBox" Width="200" IsReadOnly="True">
<TextBox.Text>
<Binding Path="BandNr" UpdateSourceTrigger="PropertyChanged" Mode="OneWay">
</Binding>
</TextBox.Text>
</TextBox>
After that I set the BandNr element in C#
BandNrTextBox.Text = "0";
Now when I select another option from my listbox normally it should get the correct value through the bindings again however it seems the Bindings are gone cause nothing appears in my textBlock.
This isn't a proper solution IMO, as you should be handling the values directly to achieve what you want. Turning the bindings on/off seems counter productive. With that said setting the .Text property changes the binding to a direct value. If you need to rebind you would do so like this.
Binding b = new Binding();
b.Source = YourViewModel; //Whatever contains BandNr
b.Path = new PropertyPath("BandNr");
b.Mode = BindingMode.OneWay;
b.UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged;
BindingOperations.SetBinding(BandNrTextBox, TextBox.TextProperty, b);
Also a heads up, you can simplify your XAML
<TextBox Name="BandNrTextBox" Width="200" IsReadOnly="True" Text="{Binding BandNr, UpdateSourceTrigger=PropertyChanged, Mode=OneWay}"/>

Change the Binding Path in a TextBlock in a DataTemplate

I have a WPF UserControl that's housed in an element host on a WindowsForms.
The WPF UserControl contains a ListBox that uses a DataTemplate that has a data bound to aTextBlock:
<UserControl.Resources>
<DataTemplate x:Key="NewsListBoxTemplate">
<TextBlock Name="tbTemplate" Padding="30,0" FontSize="28"
Text="{Binding Path=newsE}" Foreground="Blue"/>
</DataTemplate>
</UserControl.Resources>
The DataContext is based on a DataSet that gets its data from a sql server database.
I've researched and seen the various answers on SO and can identify the TextBlock at run time. But what I want to do is to change the Binding Path for that TextBlock to point to a different field of the DataSet when the user makes a choice on the Windows Form at runtime.
There are only two database fields available as choices.
From the point where I've identified the TextBlock name as tbTemplate, can anyone suggest code I can use to switch between the two Paths?
You can use BindingOperations
BindingOperations.SetBinding(tbTemplate, TextBlock.TextProperty, new Binding("MyProperty"));
Follow the link.. to get the control with its name from the DataTemplate and do the binding for that...
tbTemplate.SetBinding(TextBlock.TextProperty, new Binding("PropertyName"));

Single Property bound to multiple controls in WPF

I have a single property that consists of three letters and two numbers, such as "ABC 12". The requirements for my project ask that the UI divide this into a ComboBox for the three letter combinations and a TextBox for the numerical. I can do this pretty easily using a converters to parse out the part of the string that I need.
My question is whether there is a way to implement the "ConvertBack" logic in my converter such that I can reset the property based upon the values in the two different controls.
My xaml:
<ComboBox IsEnabled="{Binding EditMode}" ItemsSource="{Binding AbbrevsList}"
DisplayMemberPath="SelectedAbbrev" SelectedValuePath="Abbrev"
SelectedValue="{Binding Row.Code, Converter=CodeAlphaConverter,
UpdateSourceTrigger=PropertyChanged}" />
<TextBox TextAlignment="Left">
<TextBox.Text>
<Binding Path="Row.Code" Converter="CodeNumericConverter"
UpdateSourceTrigger="LostFocus">
</Binding>
</TextBox.Text>
</TextBox>
Thanks.
Since WPF does not allow you to bind converter parameters, you may have to switch your approach. Instead of using converters, other answers on SO point to changing your View Model to have the logic built into two properties and binding to those or, instead of passing in the individual value to the converter, you pass in the entire object.

TextBlock width binding to GridView

On my XAML page I have a text block with following binding:
<TextBlock Width="{Binding ActualWidth, ElementName=SessionList, Mode=OneWay}" ... />
This binds to a grid view:
<GridView x:Name="SessionList" ItemsSource="{Binding Sessions}"... />
Now when the page first loads and data is available, the text block will be visible and have the correct width. When the page loads and there is no data, the text box will not be visible because of the bound width.
But ... when I load up data in the background and after a while the data comes in (through MVVM) the list will be show, but the text block width will not change accordingly, and setting it as TwoWay has no effect.
Any ideas/tips?
ActualWidth is not a property that you can bind to within WinRT. Not sure if you are showing static text or bound text. If bound text and data is same as GridView has then it should go away if data is null. If static data, then use a ValueConverter to set the visibility of the TextBlock based on the data being null/empty
Binding issues like this are usually caused by properties that are not bindable, i.e. they are not dependency properties and/or do not implement INotifyPropertyChanged. Whatever. I use a Attached Dependency Property or, if that does not cover enough, a behavior. Now behavior are not included in WinRT, but that problem has already been addressed ;-)

I want an hourglass while my IsAsync binding is busy; possible?

I've currently got XAML code like this:
<ListView Name="fileLV" SelectionMode="Extended" ItemsSource="{Binding path=DataContext.SelectedAsset.Files,ElementName=selectionView,IsAsync=True}"/>
That "Files" property takes fifteen seconds to return. The whole time the user is wondering what's going on. I've seen some other code to show the fallback value or use multiple bindings, but those don't rely imply "leave this control alone" like an hourglass over that control would imply.
What I want is to be able name a binding and then bind some other properties to that binding's IsBusy property. I want a trigger to change the cursor on that listview while his binding is busy. Is there any existing WPF framework help for this?
I don't know of any built-in, out-of-the-box solution but there sure are ways to make a nice experience out of it.
I will give you the quick idea of how I would build this and if you need I can come up with the code as well:
Create a "LoadingItem" DataTemplate that would show an progress bar of some kind as an item of your list
Create a "DataTemplateSelector" to switch between the LoadingItem
and the RegularItem of your list.
In your Files property, clear the collection and add an item that
will be shown as LoadingItem (depends on how you built your
DataTemplateSelector logic. Start another thread to scan for files
and fill a return the results in a temporary collection
(BackgroundWorker). When the method returns, you are on the UI
thread again, clear your ItemsSource collection again and fill it
with the results.
For this do not use IsAsync. On the Property use a BackGroundWorker. First return a source with a "working message", start BackGroundWorker, then on the callback supply the real source and call NotifyPropertyChanged. You can even have a progess bar.
I was able to make the DataTemplateSelector work. One caveat was that all the bindings for the ListView need to be enumerable. In my control I added a resource like this:
<UserControl.Resources>
<x:Array x:Key="LoadingTemplate" Type="DataTemplate">
<DataTemplate>...my daisy code...</DataTemplate></x:Array>...
Then I changed my binding to look like this:
<ListView.ItemsSource>
<PriorityBinding>
<Binding Path="DataContext.SelectedAsset.Files" ElementName="selectionView" IsAsync="True"/>
<Binding Source="{StaticResource LoadingTemplate}" />
</PriorityBinding>
</ListView.ItemsSource>
Then I installed this template selector:
public class OverridableDataTemplateSelector: DataTemplateSelector
{
public override DataTemplate SelectTemplate(object item, DependencyObject container)
{
return item as DataTemplate ?? base.SelectTemplate(item, container);
}
}

Categories