WPF Command not being called from DataTemplate ContextMenu - wpf

I am stumped yet again from WPF. Sometimes things just are not straight forward! I have searched high and low for an answer but everything I have tried has failed.
I am trying to create a generic Copy and Paste menu by using a datatemplate. Then each control I wish to use that contextmenu I can just set the template of that control and provide the correct function bindings and in theory it should just work. Instead of duplicating the same menu in each custom control file. But apparently ContextMenu's are just a nightmare.
What I know:
Context menus do not inherit the data context because they are not part of the visual tree
I can use DataContext="{Binding PlacementTarget, RelativeSource={RelativeSource Self}} to get around this and it should inherit.
I cannot use WPF inspector to find out if the datacontext of the context menu is NULL. Which is annoying. Maybe there is another way. But there are no errors in the output so I have to assume its not null.
So based on my code it SHOULD WORK. Can somebody please point out the exceptionally magical combination of , "oh wait, in WPF you cant do this, and this and if this and this equals this". Answer? :)
Here is my custom control (Excuse the mess I have been hacking to try get this working!)
/// <summary>
/// Interaction logic for Pointer.xaml
/// </summary>
public partial class Expander : UserControl
{
/// <summary>
/// Command_ArrayAdd
/// </summary>
public class PasteCommand : ICommand
{
public PasteCommand(Expander _parent)
{
m_Parent = _parent;
}
/// <summary>
/// Execute
/// </summary>
public bool Execute()
{
return true;
}
/// <summary>
/// UnExecute
/// </summary>
public void UnExecute()
{
}
Expander m_Parent;
}
/// <summary>
/// Command_ArrayAdd
/// </summary>
public class CopyCommand : ICommand
{
public CopyCommand()
{
}
/// <summary>
/// Execute the change on the field
/// </summary>
public bool Execute()
{
return true;
}
/// <summary>
/// Undo the change on the field
/// </summary>
public void UnExecute()
{
}
}
/// <summary>
///
/// </summary>
public PasteCommand OnPasteCommand
{
get { return m_OnPasteCommand; }
set { m_OnPasteCommand = value; }
}
PasteCommand m_OnPasteCommand;
/// <summary>
///
/// </summary>
public CopyCommand OnCopyCommand
{
get { return m_OnCopyCommand; }
set { m_OnCopyCommand = value; }
}
CopyCommand m_OnCopyCommand;
public Expander(Database.DatabaseInstance.Struct _dbStruct, string _headerName)
{
try
{
InitializeComponent();
}
catch (System.Exception ex)
{
ErrorConsole.Instance.LogError(ex.Message + "\n" + ex.InnerException.Message);
}
m_dbStruct = _dbStruct;
DataContext = this;
m_HeaderName = _headerName;
ExpanderLabel.Content = m_HeaderName;
//AddHandler(CustomEvents.Copy, new RoutedEventHandler(OnCopyEvent));
m_OnCopyCommand = new CopyCommand();
m_OnPasteCommand = new PasteCommand(this);
}
private void OnUnloaded(object obj, RoutedEventArgs e)
{
//RemoveHandler(CustomEvents.Copy, new RoutedEventHandler(OnCopyEvent));
}
void OnMenuOpened(object obj, RoutedEventArgs e)
{
//string _clipboardData = (string)Clipboard.GetDataObject().GetData(DataFormats.Text);
//((MenuItem)ExpanderLabel.Template.FindName("PasteMenuItem", ExpanderLabel)).IsEnabled = View.ViewModel.CanCopyFromTo(_clipboardData, m_dbStruct);
}
void _OnCopyCommand()
{
Clipboard.SetDataObject(m_dbStruct.Serialized);
}
void _OnPasteCommand()
{
CommandManager.Instance.Queue(new Database.DatabaseCommands.Command_PasteStruct(m_dbStruct, null));
}
Database.DatabaseInstance.Struct m_dbStruct;
string m_HeaderName;
}
Here is my data template:
<Style x:Key="RightClickCopyPasteLabel" TargetType="{x:Type Label}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Label">
<Label>
<Label.ContextMenu>
<ContextMenu DataContext="{Binding PlacementTarget, RelativeSource={RelativeSource Self}}">
<MenuItem x:Name="CopyMenuItem" Command="{Binding DataContext.OnCopyCommand, PresentationTraceSources.TraceLevel=High}" InputGestureText="Ctrl+C" Header="Copy" />
<MenuItem x:Name="PasteMenuItem" Command="{Binding DataContext.OnPasteCommand, PresentationTraceSources.TraceLevel=High}" InputGestureText="Ctrl+P" Header="Paste" />
</ContextMenu>
</Label.ContextMenu>
</Label>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="true">
<Setter Property="FontWeight" Value="Bold"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
EDIT:
On another thread I found a way of outputting a bunch more data but unfortunately this doesn't mean anything to me. This is what I get when I right click:
System.Windows.Data Warning: 96 : BindingExpression (hash=13029119): Got PropertyChanged event from MenuItem (hash=24459401) for DataContext
System.Windows.Data Warning: 79 : BindingExpression (hash=13029119): Deactivate
System.Windows.Data Warning: 103 : BindingExpression (hash=13029119): Replace item at level 0 with {NullDataItem}
System.Windows.Data Warning: 103 : BindingExpression (hash=13029119): Replace item at level 1 with {NullDataItem}
System.Windows.Data Warning: 78 : BindingExpression (hash=13029119): Activate with root item Label (hash=25041938)
System.Windows.Data Warning: 107 : BindingExpression (hash=13029119): At level 0 using cached accessor for Label.DataContext: DependencyProperty(DataContext)
System.Windows.Data Warning: 104 : BindingExpression (hash=13029119): Replace item at level 0 with Label (hash=25041938), using accessor DependencyProperty(DataContext)
System.Windows.Data Warning: 101 : BindingExpression (hash=13029119): GetValue at level 0 from Label (hash=25041938) using DependencyProperty(DataContext): Expander (hash=20086501)
System.Windows.Data Warning: 107 : BindingExpression (hash=13029119): At level 1 using cached accessor for Expander.OnCopyCommand: RuntimePropertyInfo(OnCopyCommand)
System.Windows.Data Warning: 104 : BindingExpression (hash=13029119): Replace item at level 1 with Expander (hash=20086501), using accessor RuntimePropertyInfo(OnCopyCommand)
System.Windows.Data Warning: 101 : BindingExpression (hash=13029119): GetValue at level 1 from Expander (hash=20086501) using RuntimePropertyInfo(OnCopyCommand): CopyCommand (hash=9651034)
System.Windows.Data Warning: 80 : BindingExpression (hash=13029119): TransferValue - got raw value CopyCommand (hash=9651034)
System.Windows.Data Warning: 84 : BindingExpression (hash=13029119): TransferValue - implicit converter produced <null>
System.Windows.Data Warning: 89 : BindingExpression (hash=13029119): TransferValue - using final value <null>
System.Windows.Data Warning: 96 : BindingExpression (hash=58885068): Got PropertyChanged event from MenuItem (hash=65477567) for DataContext
System.Windows.Data Warning: 79 : BindingExpression (hash=58885068): Deactivate
System.Windows.Data Warning: 103 : BindingExpression (hash=58885068): Replace item at level 0 with {NullDataItem}
System.Windows.Data Warning: 103 : BindingExpression (hash=58885068): Replace item at level 1 with {NullDataItem}
System.Windows.Data Warning: 78 : BindingExpression (hash=58885068): Activate with root item Label (hash=25041938)
System.Windows.Data Warning: 107 : BindingExpression (hash=58885068): At level 0 using cached accessor for Label.DataContext: DependencyProperty(DataContext)
System.Windows.Data Warning: 104 : BindingExpression (hash=58885068): Replace item at level 0 with Label (hash=25041938), using accessor DependencyProperty(DataContext)
System.Windows.Data Warning: 101 : BindingExpression (hash=58885068): GetValue at level 0 from Label (hash=25041938) using DependencyProperty(DataContext): Expander (hash=20086501)
System.Windows.Data Warning: 107 : BindingExpression (hash=58885068): At level 1 using cached accessor for Expander.OnPasteCommand: RuntimePropertyInfo(OnPasteCommand)
System.Windows.Data Warning: 104 : BindingExpression (hash=58885068): Replace item at level 1 with Expander (hash=20086501), using accessor RuntimePropertyInfo(OnPasteCommand)
System.Windows.Data Warning: 101 : BindingExpression (hash=58885068): GetValue at level 1 from Expander (hash=20086501) using RuntimePropertyInfo(OnPasteCommand): PasteCommand (hash=50152377)
System.Windows.Data Warning: 80 : BindingExpression (hash=58885068): TransferValue - got raw value PasteCommand (hash=50152377)
System.Windows.Data Warning: 84 : BindingExpression (hash=58885068): TransferValue - implicit converter produced <null>
System.Windows.Data Warning: 89 : BindingExpression (hash=58885068): TransferValue - using final value <null>
System.Windows.Data Warning: 96 : BindingExpression (hash=13029119): Got PropertyChanged event from MenuItem (hash=24459401) for DataContext
System.Windows.Data Warning: 79 : BindingExpression (hash=13029119): Deactivate
System.Windows.Data Warning: 103 : BindingExpression (hash=13029119): Replace item at level 0 with {NullDataItem}
System.Windows.Data Warning: 103 : BindingExpression (hash=13029119): Replace item at level 1 with {NullDataItem}
System.Windows.Data Warning: 78 : BindingExpression (hash=13029119): Activate with root item <null>
System.Windows.Data Warning: 106 : BindingExpression (hash=13029119): Item at level 0 is null - no accessor
System.Windows.Data Warning: 103 : BindingExpression (hash=13029119): Replace item at level 1 with {NullDataItem}
System.Windows.Data Warning: 80 : BindingExpression (hash=13029119): TransferValue - got raw value {DependencyProperty.UnsetValue}
System.Windows.Data Warning: 88 : BindingExpression (hash=13029119): TransferValue - using fallback/default value <null>
System.Windows.Data Warning: 89 : BindingExpression (hash=13029119): TransferValue - using final value <null>
System.Windows.Data Warning: 96 : BindingExpression (hash=58885068): Got PropertyChanged event from MenuItem (hash=65477567) for DataContext
System.Windows.Data Warning: 79 : BindingExpression (hash=58885068): Deactivate
System.Windows.Data Warning: 103 : BindingExpression (hash=58885068): Replace item at level 0 with {NullDataItem}
System.Windows.Data Warning: 103 : BindingExpression (hash=58885068): Replace item at level 1 with {NullDataItem}
System.Windows.Data Warning: 78 : BindingExpression (hash=58885068): Activate with root item <null>
System.Windows.Data Warning: 106 : BindingExpression (hash=58885068): Item at level 0 is null - no accessor
System.Windows.Data Warning: 103 : BindingExpression (hash=58885068): Replace item at level 1 with {NullDataItem}
System.Windows.Data Warning: 80 : BindingExpression (hash=58885068): TransferValue - got raw value {DependencyProperty.UnsetValue}
System.Windows.Data Warning: 88 : BindingExpression (hash=58885068): TransferValue - using fallback/default value <null>
System.Windows.Data Warning: 89 : BindingExpression (hash=58885068): TransferValue - using final value <null>

By looking at your code everything looks OK at first glance but there is still an issue
the issue is because of incorrect ICommand interface was used to implement Commands. An implementation of System.Windows.Input.ICommand is used to bind Commands in WPF
Let me now try to explain how I figured out the issue.
look at these 3 lines from the trace
System.Windows.Data Warning: 80 : BindingExpression (hash=58885068): TransferValue - got raw value PasteCommand (hash=50152377)
above say that it got a value of type PasteCommand from the binding
System.Windows.Data Warning: 84 : BindingExpression (hash=58885068): TransferValue - implicit converter produced
above line says that it will try to convert the value received to the appropriate type i.e. 'System.Windows.Input.ICommand` in this case
System.Windows.Data Warning: 89 : BindingExpression (hash=58885068): TransferValue - using final value
above line says that it is using the final value as <null> which means that conversion to appropriate type is failed
analyzing above lines tells that the binding is correct to resolve the value however the value does not match the source type so it can't be used.
this forced me to look at the implementation of the command i.e. PasteCommand where i discovered that the implementation does not match the required interface.

Related

How can I bind RotateTransform.Angle value via a Converter?

I need to create a Converter that takes some value and returns another. This will be used to set the textblock RotateTransform.Angle value in XAML.
If I hard-code the value to a static number the textblock gets rotated successfully. But when it goes through the converter it does not get rotated.
Any insight would be appreciated.
Thanks.
Hard-coded value (works):
<TextBlock ...
<RotateTransform CenterX="0.5" CenterY="0.5">
<RotateTransform.Angle>
10
</RotateTransform.Angle>
</RotateTransform>
Going through a Converter (does not work):
<TextBlock ...
<RotateTransform CenterX="0.5" CenterY="0.5">
<RotateTransform.Angle>
<MultiBinding Converter="{StaticResource RelativeToAbsoluteRotationConverter}">
<Binding Path="RelativeAngle" />
</MultiBinding>
</RotateTransform.Angle>
</RotateTransform>
Converter class:
public class RelativeToAbsoluteRotationConverter: IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
// Add logic here... Function is hit, but no rotation ever takes place.
return 10; // irrelevant
}
// ...
Output window:
System.Windows.Data Error: 5 : Value produced by BindingExpression is not valid for target property. Int32:'30' MultiBindingExpression:target element is 'RotateTransform' (HashCode=57454947); target property is 'Angle' (type 'Double')
The solution is to modify the object Convert() method and instead of returning an int (e.g. 10), we return a double value (e.g. 10d or 10.0).
return 10d; // This works

Usercontrol UI doesn't change

UserControl
<UserControl x:Class="NolowaFrontend.Views.MainViews.SearchView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:NolowaFrontend.Views.MainViews"
xmlns:diag="clr-namespace:System.Diagnostics;assembly=WindowsBase"
xmlns:designTime="clr-namespace:NolowaFrontend.Views"
mc:Ignorable="d" Name="_this"
d:DesignHeight="300" d:DesignWidth="350" d:Background="White">
<Grid>
<TextBlock Text="{Binding SearchedUsers.Count, diag:PresentationTraceSources.TraceLevel=High}" Foreground="Red" FontSize="20"/>
</Grid>
</UserControl>
CodeBehind
public partial class SearchView : UserControl
{
private readonly SearchVM _searchVM;
public SearchView(User user)
{
InitializeComponent();
_searchVM = new SearchVM(user); ;
DataContext = _searchVM;
}
public void TimerSearch(string text)
{
_searchVM.TimerSearch(text);
}
}
ViewModel
public class SearchVM : ViewModelBase
{
private readonly User _user;
private readonly ISearchService _searchService;
private ObservableCollection<SearchedUser> _searchedUsers = new ObservableCollection<SearchedUser>();
public ObservableCollection<SearchedUser> SearchedUsers
{
get { return _searchedUsers; }
set { _searchedUsers = value; OnPropertyChanged(); }
}
public SearchVM(User user)
{
_user = user;
_searchService = new SearchService();
}
public async void TimerSearch(string text)
{
var response = await _searchService.SearchUser(text);
var data = response.ResponseData;
SearchedUsers = data.ToObservableCollection();
}
}
bindig log
System.Windows.Data Warning: 67 : BindingExpression (hash=11842506): Resolving source
System.Windows.Data Warning: 70 : BindingExpression (hash=11842506): Found data context element: TextBlock (hash=56511253) (OK)
System.Windows.Data Warning: 78 : BindingExpression (hash=11842506): Activate with root item SearchVM (hash=52539597)
System.Windows.Data Warning: 108 : BindingExpression (hash=11842506): At level 0 - for SearchVM.SearchedUsers found accessor RuntimePropertyInfo(SearchedUsers)
System.Windows.Data Warning: 104 : BindingExpression (hash=11842506): Replace item at level 0 with SearchVM (hash=52539597), using accessor RuntimePropertyInfo(SearchedUsers)
System.Windows.Data Warning: 101 : BindingExpression (hash=11842506): GetValue at level 0 from SearchVM (hash=52539597) using RuntimePropertyInfo(SearchedUsers): ObservableCollection`1 (hash=49313939 Count=0)
System.Windows.Data Warning: 108 : BindingExpression (hash=11842506): At level 1 - for ObservableCollection`1.Count found accessor RuntimePropertyInfo(Count)
System.Windows.Data Warning: 104 : BindingExpression (hash=11842506): Replace item at level 1 with ObservableCollection`1 (hash=49313939 Count=0), using accessor RuntimePropertyInfo(Count)
System.Windows.Data Warning: 101 : BindingExpression (hash=11842506): GetValue at level 1 from ObservableCollection`1 (hash=49313939 Count=0) using RuntimePropertyInfo(Count): '0'
System.Windows.Data Warning: 80 : BindingExpression (hash=11842506): TransferValue - got raw value '0'
System.Windows.Data Warning: 84 : BindingExpression (hash=11842506): TransferValue - implicit converter produced '0'
System.Windows.Data Warning: 89 : BindingExpression (hash=11842506): TransferValue - using final value '0'
System.Windows.Data Warning: 56 : Created BindingExpression (hash=29516363) for Binding (hash=55475379) BindingExpression:Path=SearchedUsers.Count; DataItem=null;
System.Windows.Data Warning: 58 : Path: 'SearchedUsers.Count'
System.Windows.Data Warning: 60 : BindingExpression (hash=29516363): Default mode resolved to OneWay
System.Windows.Data Warning: 61 : BindingExpression (hash=29516363): Default update trigger resolved to PropertyChanged
System.Windows.Data Warning: 62 : BindingExpression (hash=29516363): Attach to System.Windows.Controls.TextBlock.Text (hash=35990092)
System.Windows.Data Warning: 67 : BindingExpression (hash=29516363): Resolving source
System.Windows.Data Warning: 70 : BindingExpression (hash=29516363): Found data context element: TextBlock (hash=35990092) (OK)
System.Windows.Data Warning: 71 : BindingExpression (hash=29516363): DataContext is null
System.Windows.Data Warning: 65 : BindingExpression (hash=29516363): Resolve source deferred
System.Windows.Data Warning: 67 : BindingExpression (hash=29516363): Resolving source
System.Windows.Data Warning: 70 : BindingExpression (hash=29516363): Found data context element: TextBlock (hash=35990092) (OK)
System.Windows.Data Warning: 78 : BindingExpression (hash=29516363): Activate with root item SearchVM (hash=64320678)
System.Windows.Data Warning: 107 : BindingExpression (hash=29516363): At level 0 using cached accessor for SearchVM.SearchedUsers: RuntimePropertyInfo(SearchedUsers)
System.Windows.Data Warning: 104 : BindingExpression (hash=29516363): Replace item at level 0 with SearchVM (hash=64320678), using accessor RuntimePropertyInfo(SearchedUsers)
System.Windows.Data Warning: 101 : BindingExpression (hash=29516363): GetValue at level 0 from SearchVM (hash=64320678) using RuntimePropertyInfo(SearchedUsers): ObservableCollection`1 (hash=42592467 Count=0)
System.Windows.Data Warning: 107 : BindingExpression (hash=29516363): At level 1 using cached accessor for ObservableCollection`1.Count: RuntimePropertyInfo(Count)
System.Windows.Data Warning: 104 : BindingExpression (hash=29516363): Replace item at level 1 with ObservableCollection`1 (hash=42592467 Count=0), using accessor RuntimePropertyInfo(Count)
System.Windows.Data Warning: 101 : BindingExpression (hash=29516363): GetValue at level 1 from ObservableCollection`1 (hash=42592467 Count=0) using RuntimePropertyInfo(Count): '0'
System.Windows.Data Warning: 80 : BindingExpression (hash=29516363): TransferValue - got raw value '0'
System.Windows.Data Warning: 84 : BindingExpression (hash=29516363): TransferValue - implicit converter produced '0'
System.Windows.Data Warning: 89 : BindingExpression (hash=29516363): TransferValue - using final value '0'
System.Windows.Data Warning: 95 : BindingExpression (hash=11842506): Got PropertyChanged event from SearchVM (hash=52539597)
System.Windows.Data Warning: 101 : BindingExpression (hash=11842506): GetValue at level 0 from SearchVM (hash=52539597) using RuntimePropertyInfo(SearchedUsers): ObservableCollection`1 (hash=53428882 Count=2)
System.Windows.Data Warning: 105 : BindingExpression (hash=11842506): Item at level 1 has same type - reuse accessor RuntimePropertyInfo(Count)
System.Windows.Data Warning: 104 : BindingExpression (hash=11842506): Replace item at level 1 with ObservableCollection`1 (hash=53428882 Count=2), using accessor RuntimePropertyInfo(Count)
System.Windows.Data Warning: 101 : BindingExpression (hash=11842506): GetValue at level 1 from ObservableCollection`1 (hash=53428882 Count=2) using RuntimePropertyInfo(Count): '2'
System.Windows.Data Warning: 80 : BindingExpression (hash=11842506): TransferValue - got raw value '2'
System.Windows.Data Warning: 84 : BindingExpression (hash=11842506): TransferValue - implicit converter produced '2'
System.Windows.Data Warning: 89 : BindingExpression (hash=11842506): TransferValue - using final value '2'
Hi! When if there is a function that calls SearchView.TimerSearch("x")
I think UserControl shows the count that is returned from SearchView.TimerSearch("x")
But it's not work how I expect.
binding log says it changed to 2. but it shows '0' and never changed!
Can someone help me?
You are binding to the nested Count property of the collection. But since you are replacing the complete collection, the Count property effectively does not change as the collection instance change is not detected by the Binding. In other words, the Binding is listening for a Count changed event, but this event is never raised, because the collection instance itself has changed instead.
It's generally recommended to always use the same collection instance in a data binding context: clear the collection and add the new range of items.
Next is your async method TimerSearch: it is currently not awaited and does not return a Task!
Remember: async void is only allowed for event handlers. Otherwise, a void method must always be converted to return Task.
When you await a method you must await the complete call tree (all the callers too): async Task everywhere.
// Make method return Task
public async Task TimerSearchAsync(string text)
{
var response = await _searchService.SearchUser(text);
SearchedUsers.Clear();
foreach (var data in response.ResponseData)
{
SearchedUsers.Add(data);
}
}
In case response.ResponseData returns a List<T>, you can replace the foreach loop with a call of the List<T>.ForEach method to compact the code:
response.ResponseData.ForEach(SearchedUsers.Add);
Next, convert the calling method to return a Task too and await the TimerSearch method call:
// Declare method async and return a Task
public async Task TimerSearcAsynch(string text)
{
await _searchVM.TimerSearchAsync(text);
}
Finally, avoid setting the DataContext in your control. This limits the usability of the control to be only used in the special context.
Generally, you don't have a view model class for each control, this is not how you design controls. Controls must be developed independent from a view model class.
In your case, the control would have a UserCount dependency property, which you later can bind to an external view model. Internally, the TextBox would bind to the UserCount dependency property.

Binding to TimeSpan field with IMultiValueConverter

I am getting an exception for this binding
<TextBlock.Text>
<MultiBinding Converter="{StaticResource converter}" Mode="OneWay">
<Binding Path="TimeSpanProperty" />
<Binding Path="FormsttingOption" />
</MultiBinding>
</TextBlock.Text>
// converter.Convert code
if (formsttingOption == DurationFormat.Minutes)
return Math.Round(timeSpan.TotalMinutes);
else
return duration;
Value produced by BindingExpression is not valid for target property.; Value='164' MultiBindingExpression:target element is 'TextBlock' (Name=''); target property is 'Text' (type 'String')
The converter will, based on formatting option, convert TimeSpan value. In above example, FormattingOption is minutes, which means that TimeSpan value will be converted to minutes (164).
For some reason Text property does not accept this value. Anyone knows the reason, and solution?
I think the error explains it quite nicely:
Value produced by BindingExpression is not valid for target property.; Value='164' MultiBindingExpression:target element is 'TextBlock' (Name=''); target property is 'Text' (type 'String')
It tells you that TextBlock.Text is of type String and your converter is not producting a valid value.
In your IMultiValueConverter.Convert method, ensure you are returning a String, i.e.
public object Convert(object[] values, Type targetType, Object parameter, CultureInfo culture)
{
var timeSpan = (TimeSpan)values[0];
var formattingOption = (DurationFormat)values[1];
if(formattingOption == DurationFormat.Minutes)
{
return Math.Round(timeSpan.TotalMinutes).ToString(culture);
}
return duration.ToString(); // TODO not sure where duration comes from
}

Understanding System.Windows.Data Error: 23 : Cannot convert

I've struggled to understand this error for some time and now it's finally breaking something. Here's a dumbed-down sketch of what I'm doing:
My datawrapper class:
class DummyWrapper<T> : INotifyPropertyChanged
{
public DummyWrapper() { }
private T _data;
public T Data
{
get { return _data; }
set
{
_data = value;
Notify("Data");
}
}
public static implicit operator T(DummyWrapper<T> d)
{
return d._data;
}
...
}
My XAML:
<DockPanel>
<ContentPresenter Name="cp" Visibility="Collapsed"/>
<Rectangle Name="foo" Fill="{Binding ElementName=cp, Path=Content}" Height="50" Width="50"/>
</DockPanel>
The pertinent bit of my codebehind:
DummyWrapper<Brush> dw = new DummyWrapper<Brush>(new SolidColorBrush((Color)ColorConverter.ConvertFromString("Red")));
cp.Content = dw;
And of course if this was all working the way I expected I wouldn't be here. The output window shows me this:
System.Windows.Data Error: 23 : Cannot convert 'WpfTestApplication.DummyWrapper`1[System.Windows.Media.Brush]'
from type 'DummyWrapper`1' to type 'System.Windows.Media.Brush'
for 'en-US' culture with default conversions; consider using Converter property of Binding.
... and it goes on in that vein for some time.
Is there something I can do to allow my DummyWrapper to be converted automatically (ie w/o supplying a converter in the binding) in this context?
Change your line of code to
var solidColorBrush = new SolidColorBrush((Color) ColorConverter.ConvertFromString("Red"));
DummyWrapper<Brush> dw = new DummyWrapper<Brush>(solidColorBrush);
cp.Content = (Brush)dw;
You don't want to use converters that's fine. Content is an object and doing an implicit operator will not just do that unless the Content is of type Brush. You have to explicitly cast it to Brush
System.Object is the type of the Content property.
Through inheritance there's already an implicit conversion to the base
type

"BindingExpression path error" using ItemsControl and VirtualizingStackPanel

I'm using Silverlight on Windows Phone 7.
Is it normal to get loads of "BindingExpression path error" debug messages when using a VirtualizingStackPanel? I think it is happening because the visual items are temporarily unbound from the data items collection as they are recycled ...
I have an ItemsControl whose ItemsPanel's ItemsPanelTemplate is a VirtualizingStackPanel. It binds to a "Notes" ObservableCollection on my ViewModel.
<ItemsControl x:Name="ListView" ItemsSource="{Binding Notes}"
ItemTemplate="{StaticResource ListDataTemplate}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<VirtualizingStackPanel/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.Template>
<ControlTemplate>
<ScrollViewer x:Name="ScrollViewer" Padding="{TemplateBinding Padding}">
<ItemsPresenter/>
</ScrollViewer>
</ControlTemplate>
</ItemsControl.Template>
</ItemsControl>
This is within a UserControl whose DataContext is set to a MainViewModel ViewModel.
The ListDataTemplate contains a Button, which itself contains some text, data-bound to a property of the items in the Notes ObservableCollection:
<DataTemplate x:Key="ListDataTemplate">
<Button>
<Grid>
<TextBlock TextWrapping="Wrap" Text="{Binding Title}">
</Grid>
</Button>
</DataTemplate>
When I run my program it all works fine - only a small number of items in my Notes collection are bound to at any one time. However I get lots of debug errors as I scroll up and down:
System.Windows.Data Error: BindingExpression path error: 'Title' property not found on 'EnWp7.ViewModels.MainViewModel' 'EnWp7.ViewModels.MainViewModel' (HashCode=119211466). BindingExpression: Path='Title' DataItem='EnWp7.ViewModels.MainViewModel' (HashCode=119211466); target element is 'System.Windows.Controls.TextBlock' (Name=''); target property is 'Text' (type 'System.String')..
System.Windows.Data Error: BindingExpression path error: 'Title' property not found on 'EnWp7.ViewModels.MainViewModel' 'EnWp7.ViewModels.MainViewModel' (HashCode=119211466). BindingExpression: Path='Title' DataItem='EnWp7.ViewModels.MainViewModel' (HashCode=119211466); target element is 'System.Windows.Controls.TextBlock' (Name=''); target property is 'Text' (type 'System.String')..
System.Windows.Data Error: BindingExpression path error: 'Title' property not found on 'EnWp7.ViewModels.MainViewModel' 'EnWp7.ViewModels.MainViewModel' (HashCode=119211466). BindingExpression: Path='Title' DataItem='EnWp7.ViewModels.MainViewModel' (HashCode=119211466); target element is 'System.Windows.Controls.TextBlock' (Name=''); target property is 'Text' (type 'System.String')..
System.Windows.Data Error: BindingExpression path error: 'Title' property not found on 'EnWp7.ViewModels.MainViewModel' 'EnWp7.ViewModels.MainViewModel' (HashCode=119211466). BindingExpression: Path='Title' DataItem='EnWp7.ViewModels.MainViewModel' (HashCode=119211466); target element is 'System.Windows.Controls.TextBlock' (Name=''); target property is 'Text' (type 'System.String')..
It is looking for the "Title" property on the main view model (whose Notes property is the ItemsSource) - I think this is normal - it is a transient situation as the visual items are unbound from items in the Notes collection in order to be recycled by assigning them to another item in the collection.
Has anyone else seen this? Am I right that it is normal?
If I create a dummy Title property on my MainViewModel and set a debug breakpoint then I see this stack trace. Note the "UnlinkContainerFromItem":
...!EnWp7.ViewModels.MainViewModel.Title.get() Line 54 C#
mscorlib.dll!System.Reflection.RuntimeMethodInfo.InternalInvoke(System.Reflection.RuntimeMethodInfo rtmi = {System.Reflection.RuntimeMethodInfo}, object obj = {EnWp7.ViewModels.MainViewModel}, System.Reflection.BindingFlags invokeAttr = Default, System.Reflection.Binder binder = null, object parameters = null, System.Globalization.CultureInfo culture = null, bool isBinderDefault = false, System.Reflection.Assembly caller = null, bool verifyAccess = true, ref System.Threading.StackCrawlMark stackMark = LookForMyCaller)
mscorlib.dll!System.Reflection.RuntimeMethodInfo.InternalInvoke(object obj = {EnWp7.ViewModels.MainViewModel}, System.Reflection.BindingFlags invokeAttr = Default, System.Reflection.Binder binder = null, object[] parameters = null, System.Globalization.CultureInfo culture = null, ref System.Threading.StackCrawlMark stackMark = LookForMyCaller) + 0x14e bytes
mscorlib.dll!System.Reflection.RuntimePropertyInfo.InternalGetValue(System.Reflection.PropertyInfo thisProperty = {System.Reflection.RuntimePropertyInfo}, object obj = {EnWp7.ViewModels.MainViewModel}, object[] index = null, ref System.Threading.StackCrawlMark stackMark = LookForMyCaller) + 0x4e bytes
mscorlib.dll!System.Reflection.RuntimePropertyInfo.GetValue(object obj = {EnWp7.ViewModels.MainViewModel}, object[] index = null) + 0x2 bytes
System.Windows.dll!System.Windows.CLRPropertyListener.Value.get() + 0x1b bytes
System.Windows.dll!System.Windows.PropertyAccessPathStep.ConnectToPropertyInSource() + 0x148 bytes
System.Windows.dll!System.Windows.PropertyAccessPathStep.ConnectToProperty() + 0x16 bytes
System.Windows.dll!System.Windows.PropertyAccessPathStep.ReConnect(object newSource = {EnWp7.ViewModels.MainViewModel}) + 0x13 bytes
System.Windows.dll!System.Windows.PropertyPathListener.ReConnect(object source = {EnWp7.ViewModels.MainViewModel}) + 0x10 bytes
System.Windows.dll!System.Windows.Data.BindingExpression.SourceAquired() + 0x11 bytes
System.Windows.dll!System.Windows.Data.BindingExpression.DataContextChanged(object o = {System.Windows.Controls.TextBlock}, System.Windows.DataContextChangedEventArgs e = {System.Windows.DataContextChangedEventArgs}) + 0x26 bytes
System.Windows.dll!System.Windows.FrameworkElement.OnDataContextChanged(System.Windows.DataContextChangedEventArgs e = {System.Windows.DataContextChangedEventArgs}) + 0x15 bytes
System.Windows.dll!System.Windows.FrameworkElement.OnAncestorDataContextChanged(System.Windows.DataContextChangedEventArgs e = {System.Windows.DataContextChangedEventArgs}) + 0x11 bytes
System.Windows.dll!System.Windows.FrameworkElement.NotifyDataContextChanged(System.Windows.DataContextChangedEventArgs e = {System.Windows.DataContextChangedEventArgs}) + 0x59 bytes
System.Windows.dll!System.Windows.FrameworkElement.OnAncestorDataContextChanged(System.Windows.DataContextChangedEventArgs e = {System.Windows.DataContextChangedEventArgs}) + 0x18 bytes
System.Windows.dll!System.Windows.FrameworkElement.NotifyDataContextChanged(System.Windows.DataContextChangedEventArgs e = {System.Windows.DataContextChangedEventArgs}) + 0x59 bytes
System.Windows.dll!System.Windows.FrameworkElement.OnAncestorDataContextChanged(System.Windows.DataContextChangedEventArgs e = {System.Windows.DataContextChangedEventArgs}) + 0x18 bytes
System.Windows.dll!System.Windows.FrameworkElement.NotifyDataContextChanged(System.Windows.DataContextChangedEventArgs e = {System.Windows.DataContextChangedEventArgs}) + 0x59 bytes
System.Windows.dll!System.Windows.FrameworkElement.OnAncestorDataContextChanged(System.Windows.DataContextChangedEventArgs e = {System.Windows.DataContextChangedEventArgs}) + 0x18 bytes
System.Windows.dll!System.Windows.FrameworkElement.NotifyDataContextChanged(System.Windows.DataContextChangedEventArgs e = {System.Windows.DataContextChangedEventArgs}) + 0x59 bytes
System.Windows.dll!System.Windows.FrameworkElement.OnAncestorDataContextChanged(System.Windows.DataContextChangedEventArgs e = {System.Windows.DataContextChangedEventArgs}) + 0x18 bytes
System.Windows.dll!System.Windows.FrameworkElement.NotifyDataContextChanged(System.Windows.DataContextChangedEventArgs e = {System.Windows.DataContextChangedEventArgs}) + 0x59 bytes
System.Windows.dll!System.Windows.FrameworkElement.OnAncestorDataContextChanged(System.Windows.DataContextChangedEventArgs e = {System.Windows.DataContextChangedEventArgs}) + 0x18 bytes
System.Windows.dll!System.Windows.FrameworkElement.NotifyDataContextChanged(System.Windows.DataContextChangedEventArgs e = {System.Windows.DataContextChangedEventArgs}) + 0x59 bytes
System.Windows.dll!System.Windows.FrameworkElement.OnAncestorDataContextChanged(System.Windows.DataContextChangedEventArgs e = {System.Windows.DataContextChangedEventArgs}) + 0x18 bytes
System.Windows.dll!System.Windows.FrameworkElement.NotifyDataContextChanged(System.Windows.DataContextChangedEventArgs e = {System.Windows.DataContextChangedEventArgs}) + 0x59 bytes
System.Windows.dll!System.Windows.FrameworkElement.OnPropertyChanged(System.Windows.DependencyProperty dp = {System.Windows.CoreDependencyProperty}) + 0x1d bytes
System.Windows.dll!System.Windows.DependencyObject.RaisePropertyChangeNotifications(System.Windows.DependencyProperty dp = {System.Windows.CoreDependencyProperty}, object newValue = {EnWp7.ViewModels.MainViewModel}, object oldValue = {EnWp7.Store.NoteLocal}) + 0x38 bytes
System.Windows.dll!System.Windows.DependencyObject.ClearValueInternal(System.Windows.DependencyProperty dp = {System.Windows.CoreDependencyProperty}) + 0x138 bytes
System.Windows.dll!System.Windows.DependencyObject.ClearValue(System.Windows.DependencyProperty dp = {System.Windows.CoreDependencyProperty}) + 0x7 bytes
System.Windows.dll!System.Windows.Controls.ItemContainerGenerator.UnlinkContainerFromItem(System.Windows.DependencyObject container = {System.Windows.Controls.ContentPresenter}, object item = {EnWp7.Store.NoteLocal}, bool isRecycling = true) + 0x1f bytes
System.Windows.dll!System.Windows.Controls.ItemContainerGenerator.Remove(System.Windows.Controls.Primitives.GeneratorPosition position = {System.Windows.Controls.Primitives.GeneratorPosition}, int count = 2, bool isRecycling = true) + 0x14a bytes
System.Windows.dll!System.Windows.Controls.ItemContainerGenerator.System.Windows.Controls.Primitives.IRecyclingItemContainerGenerator.Recycle(System.Windows.Controls.Primitives.GeneratorPosition position = {System.Windows.Controls.Primitives.GeneratorPosition}, int count = 2) + 0x9 bytes
System.Windows.dll!System.Windows.Controls.VirtualizingStackPanel.CleanupRange(int startIndex = 0, int count = 2) + 0x20 bytes
System.Windows.dll!System.Windows.Controls.VirtualizingStackPanel.CleanupContainers(System.Windows.Controls.ItemsControl itemsControl = {System.Windows.Controls.ItemsControl}) + 0x73 bytes
System.Windows.dll!System.Windows.Controls.VirtualizingStackPanel.MeasureOverride(System.Windows.Size constraint = {System.Windows.Size}) + 0xe4 bytes
System.Windows.dll!System.Windows.FrameworkElement.MeasureOverride(System.IntPtr nativeTarget = 102509504, float inWidth = 480.0, float inHeight = 499.0, out float outWidth = 0.0, out float outHeight = 0.0) + 0x45 bytes
Thanks,
Damian
I think your assessment of the situation is correct, but it sounds like a bug in the way the VirtualizingStackPanel works. It's not affecting the execution or bindings of your application at the points where you need it, but raising exceptions is a relatively expensive operation and so it would make sense to update the appropriate DataContext accordingly when unlinking the containers.
I think the best place to raise it as a potential bug would be on the App Hub forums
I know this post is pretty old, but a solution without creating the dummy properties is making sure the DataContext of the items can only be of the type you expect.
I do this with the following converter on the root of the data template:
public class SpecificTypeConverter : IValueConverter
{
public string Type { get; set; }
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value == null || value.GetType().FullName == this.Type)
{
return value;
}
return DependencyProperty.UnsetValue;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
Add a static resource like this:
<p:PhoneApplicationPage.Resources>
<converters:SpecificTypeConverter x:Key="MustBeItem" Type="My.Namespace.MyListItemViewModel" />
</p:PhoneApplicationPage.Resources>
And in the data template, use the converter:
<DataTemplate>
<StackPanel DataContext="{Binding Converter={StaticResource MustBeItem}}" >
<TextBlock Tekst="{Binding Path=Name}" />
</StackPanel>
</DataTemplate>
Now the Name property of the parent will never be checked.

Resources