Fake XAML designer errors when binding to dynamic objects and indexed properties - wpf

The following two classes are given (F#)
type DynamicHelper() =
inherit DynamicObject()
...
override this.TryGetMember(binder: GetMemberBinder, result: obj byref) = ...
type IndexedHelper() =
...
member this.IndexedProperty with get(index: string) : float32 = ...
Instances of these classes are used in WPF ViewModel classes and in XAML binding.
<GridViewColumn Header="DynamicHelper" DisplayMemberBinding="{Binding DynamicHelperInstance.AProperty}" Width="Auto"/>
<GridViewColumn Header="IndexedProperty" DisplayMemberBinding="{Binding IndexedHelperInstance.IndexedProperty[anIndex]}" Width="Auto"/>
The code compiles and runs fine, however the XAML designed in Visual Studio 2022 gives the following Intellisense Errors for the two bindings
Error XLS0432 The property 'AProperty' was not found in type 'DynamicHelper'.
Error XLS0521 Type 'Single?' is not a collection.
How can I avoid or suppress these binding errors in the XAML designer?

Related

ValueConverter behaves like singleton

I have a ValueConverter which needs an external stateful module to work. So I've inherited it from Freezable and declare dependency propery.
public class Decorator : Freezable, IValueConverter
{
public static readonly DependencyProperty HighlighterProperty =
DependencyProperty.Register("Highlighter", typeof (IHighlighter), typeof (Decorator), new PropertyMetadata(null));
public ITypeNameHighlighter TypeNameHighlighter
{
get { return (ITypeNameHighlighter)GetValue(TypeNameHighlighterProperty); }
set { SetValue(TypeNameHighlighterProperty, value); }
}
//...
}
Then I use DataTemplate and DataTemplateSelector to display a View.
Create an instance of Decorator in resources and use it in binding
<DataTemplate x:Key="ViewTemplate">
...
<ListView ...>
<Control.Resources>
<GUI:Decorator x:Key="Decorator" **Highlighter="{Binding Highlighter}"** />
</Control.Resources>
...
<GridViewColumn>
<GridViewColumnHeader ... />
<GridViewColumn.CellTemplate>
<DataTemplate>
<GUI:RichTextBlock RichText="{Binding Path=Title, Converter={**StaticResource Decorator**}}" />
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
</ListView>
...
</DataTemplate>
Problem is when several instances of View are created all of them uses one (first) instance of Decorator to convert an entity to RichText (while in fact several instances of Decorator are created - one by view).
Highlighter has a state and that is why it is needed that each View must use its own instance of Decorator.
Any comments why there is such behavior?
Any ideas how to fix it?
UPDATE:
D J asked me "why you need multiple instances of converter" so I'll describe the task.
There are several instances of the same View in the application. Each View contains its own text filter to filter out elements from the ListView. There is a Highlighter on the ViewModel side which has a filter text and string (ListView element title in our case) as an input and returns information what parts of title matches the filter text. Decorator converts has a title and information returned from the Highlighter as an input and RichText as the output.
I agree that ValueConverter in the form as it offered by WPF is not very suitable for the problem. But I do not see any other elegant way to do it on the View side.
Ed.ward
Put x:Shared=False where you declare the resource of converter. On everycall it will give you new object.
<GUI:Decorator x:Shared=False x:Key="Decorator" **Highlighter="{Binding Highlighter}"** />

Is it possible to databind directly from a linq query result to a control?

I have just started using WPF and am having trouble data binding from the result of a Linq query to a ListView.
I have tried a number of combinations including setting the DataContext and ItemsSource to the query. As in:
listView.DataContext = (from person in People select person).ToList();
Then in the xaml setting the DisplayMemberBinding to {Binding Name} or {Binding /Name} etc.
I'm not worried about any updating either way other than just showing a list of items from the query at this stage.
So I guess i'm missing some pretty basic knowledge with WPF but this part of it seems to have a rather steep learning curve so maybe a nudge in the right direction of some example code would be good. It seems that most code involves a lot of creation of dataviews or notifying datatypes or at least binding to local objects rather than straight from a query.
Try instead:
listView.ItemsSource = (from person in People select person).ToList();
[DataContext sets the binding context for the control and its children. ItemsSource sets the collection used to generate the content of the items in the control.]
You could also simply:
listView.ItemsSource = People;
Fuller example:
MainWindow.xaml:
<Window x:Class="WpfApplication2.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 x:Name="listView">
<ListView.View>
<GridView>
<GridViewColumn DisplayMemberBinding="{Binding Name}"/>
<GridViewColumn DisplayMemberBinding="{Binding Age}"/>
</GridView>
</ListView.View>
</ListView>
</Grid>
</Window>
MainWindow.xaml.cs:
using System.Windows;
namespace WpfApplication2
{
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
var people = new[] { new { Name = "John", Age = 40 }, new { Name = "Bill", Age = 50 } };
listView.ItemsSource = people;
}
}
}

DependencyProperty of Type Delegate

I have created an attached behavior that is used to execute a Delegate of type Func<bool> when the behavior is invoked. Below is the dependancy property definition.
public static readonly DependencyProperty SendToDetailBehaviorProperty = DependencyProperty.RegisterAttached("SendToDetailBehavior", typeof(Func<bool>), typeof(ListDetailAspectSendToDetailBehavior), new UIPropertyMetadata(null, SendToDetail));
I have it working just as expected however in my XAML I get the following error, preventing the designer from loading.
Property 'SendToDetailBehavior' was
not found or is not serializable for
type 'SortableListView'
Below you will find the xaml.
<Controls:SortableListView Grid.Row="0"
Grid.Column="0"
Name="lvwLocations"
MinHeight="150"
MinWidth="{Binding Path=BusinessObject.Locations, ValidatesOnDataErrors=true, Converter={StaticResource AlwaysReturn1Converter}, Mode=OneWay, UpdateSourceTrigger=PropertyChanged}"
Style="{DynamicResource SortableListViewStyle}"
ScrollViewer.VerticalScrollBarVisibility="Auto"
ScrollViewer.HorizontalScrollBarVisibility="Auto"
IsSynchronizedWithCurrentItem="True"
**behaviors:ListDetailAspectSendToDetailBehavior.SendToDetailBehavior="{Binding Path=LocationListDetail.SendFocusToDetail}"**
ItemsSource="{Binding Path=LocationListDetail.MasterList}"
SelectedItem="{Binding Path=LocationListDetail.DetailItem, Mode=TwoWay}"
MouseDoubleClick="lvwLocations_MouseDoubleClick">
If I change the underlying type of the Dependancy Property to a bool for example, the error goes away.
As I said the attached behavior is working, only the designer blows up. I have looked for documentation on this and have come up empty. I am hoping someone here has some insight.
Thanks,
BDN
Instead of using delegate, change your dependency property type to Command (I used the DelegateCommand) which will wrap the delegate inside it. I was having the same problem as yours, but it was solved by this method.
Is this happening in VS 2008, 2010, or Expression Blend? The VS2008 designer is notoriously fragile. As far as fixing it, have you tried using a non-generic delegate type? Something like so:
public delegate bool SendToDetail();
And then your VM would expose a property of that delegate type instead of Func<bool>.

ReorderListView in WPF

I love the controls from the Bag-Of-Tricks.
I am interested in modifying the ReorderListBox control to get a ReorderListView control. However, simply changing the base class from ListBox to ListView is not working.
When I try to add a ReorderListView to XAML like this:
<lib:ReorderListView Grid.Row="1">
<ReorderListView.View>
<GridView>
<GridViewColumn Header="Data1" />
</GridView>
</ReorderListView.View>
</lib:ReorderListView>
I get an error ("The tag 'ReorderListView.View' does not exist in XML namespace 'http://schemas.microsoft.com/winfx/2006/xaml/presentation'.)
How do I modify the ReorderListBox example to get this to work? Has anyone already succeeded in doing this?
It looks like you forgot to specify the 'lib' namespace of your ReorderListView.View property
<lib:ReorderListView Grid.Row="1">
<lib:ReorderListView.View>
<GridView>
<GridViewColumn Header="Data1" />
</GridView>
</lib:ReorderListView.View>

How do I handle foreign keys with WPF databinding?

I have a ListView in WPF that is databound to a basic table that I pull from the database. The code for the ListView is as follows:
<ListView Canvas.Left="402" Canvas.Top="480" Height="78" ItemsSource="{Binding}" Name="lsvViewEditCardPrint" Width="419">
<ListView.View>
<GridView>
<GridViewColumn DisplayMemberBinding="{Binding Path=IdCst}">Set</GridViewColumn>
<GridViewColumn DisplayMemberBinding="{Binding Path=Language}">Language</GridViewColumn>
<GridViewColumn DisplayMemberBinding="{Binding Path=Number}">Number</GridViewColumn>
<GridViewColumn DisplayMemberBinding="{Binding Path=IdArt}">Artwork</GridViewColumn>
</GridView>
</ListView.View>
</ListView>
The IdCst column is a foreign key to a separate table, and I'd like to display the actual name field from that table instead of just the Id. Does anybody know how to set a databinding, or is there an event, such as OnItemDataBound, that I could intercept to modify the display?
This blog post may help:
...I assumed the foreign key should be bound to the ‘SelectedValue’ property,
and there is an ItemSource that I can
bind to my fact table so the drop down
is populated.
At this point my dropdown worked, but
nothing would appear in the combobox.
I finally noticed a ‘SelectedItemPath’
property - I assumed this would be the
name of the field in my dropdown that
was associated to my foreign key. Sure
enough, that’s exactly what it is.
I'd add a new property to your underlying class:
Public ReadOnly Property NameCst() as String
Get
Return Names.LookupName(Me.IdCst)
End Get
End Property
or something similar. Note that you'll probably have to include a Notify Property Changed event in your .IdCst setter for "NameCst".
An alternative is to write a ValueConverter that does the lookup, but that's pretty heavy weight for something so simple.
BOO YAH!!!
I looked at the samples here, dug off a few of the references from other posts here, and found the answer... IValueConverter ... an interface that can be used with WPF that will convert values at the point of binding. It is a little tricky to put together at first, but not that difficult.
The first step is to create a simple lookup or converter class that implements the IValueConverter interface. For my solution, I did this:
Namespace TCRConverters
Public Class SetIdToNameConverter
Implements IValueConverter
Public Function Convert(ByVal value As Object, ByVal targetType As System.Type, ByVal parameter As Object, ByVal culture As System.Globalization.CultureInfo) As Object Implements System.Windows.Data.IValueConverter.Convert
Dim taCardSet As New TCRTableAdapters.CardSetTableAdapter
Return taCardSet.GetDataById(DirectCast(value, Integer)).Item(0).Name
End Function
Public Function ConvertBack(ByVal value As Object, ByVal targetType As System.Type, ByVal parameter As Object, ByVal culture As System.Globalization.CultureInfo) As Object Implements System.Windows.Data.IValueConverter.ConvertBack
Return Nothing
End Function
End Class
End Namespace
Note: I am not utilizing the ConvertBack method, but it is required by the interface.
From there you need to add a reference to the namespace in your XAML header section:
<Window x:Class="Main" Loaded="Main_Loaded"
// Standard references here...
xmlns:c="clr-namespace:TCR_Editor.TCRConverters"
Title="TCR Editor" Height="728" Width="1135" Name="Main">
Then in your Windows.Resources section, you can reference the converter, and in my case, I created a static reference to the CollectionViewSource that would be storing the data:
<Window.Resources>
<CollectionViewSource Source="{Binding Source={x:Static Application.Current}, Path=CardDetails}" x:Key="CardDetails">
</CollectionViewSource>
<c:SetIdToNameConverter x:Key="SetConverter"/>
</Window.Resources>
Then finally, in the ListView that was part of the initial problem, you add the converter reference:
<ListView Canvas.Left="402" Canvas.Top="480" Height="78" ItemsSource="{Binding}" Name="lsvViewEditCardPrint" Width="419">
<ListView.View>
<GridView>
<GridViewColumn DisplayMemberBinding="{Binding Path=IdCst, Converter={StaticResource SetConverter}}">Set</GridViewColumn>
// Other Columns here...
</GridView>
</ListView.View>
</ListView>
So the great part now is that when I trigger an event that has a Card Id, all I need to do is reset set the CollectionViewSource...
DirectCast(Me.FindResource("CardDetails"), CollectionViewSource).Source = taCardDetails.GetDataById(CardId)
...and all the binding elements of WPF do the rest!
The nice thing about is is that I can easily create other converters, add them to various DataTemplates or columns elsewhere in the application, and once I get all of the data into the WPF app itself, the conversions can be conducted without going to the database.

Resources