I am trying to bind a ComboBox to the named cells of a SpreadsheetGear worksheet.
SpreadsheetGear is an obfuscated assembly, so that i my first guess.
<ComboBox Width="200" x:Name="comboBox" IsEditable="True" ItemsSource="{Binding Names, Mode=OneWay}">
<ComboBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Name}"/>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
and the view-model propery is
private IWorksheet worksheet;
public IWorksheet Worksheet
{
get { return worksheet; }
private set { SetField(ref worksheet, value, () => Worksheet); OnPropertyChanged(() => Names); }
}
public IEnumerable<IName> Names
{
get { return Worksheet.Names.Cast<IName>(); }
}
I am getting the following error in my Output window
System.Windows.Data Error: 40 : BindingExpression path error: 'Name' property not found on 'object' ''ᜪ' (HashCode=1500138080)'. BindingExpression:Path=Name; DataItem='ᜪ' (HashCode=1500138080); target element is 'TextBlock' (Name=''); target property is 'Text' (type 'String')
I've tried returning Worksheet.Names directly, which doesn't inherit from Enumerable but DOES provide GetEnumerator(). That yielded the same error.
Any ideas?
Without more code, it's hard to say, but I'll take a random guess: Is IName an internal interface? Most code obfuscators will only mangle internal/private/protected classes/enums/interfaces...
Related
I try to use binding with an attached property. But can't get it working.
public class Attached
{
public static DependencyProperty TestProperty =
DependencyProperty.RegisterAttached("TestProperty", typeof(bool), typeof(Attached),
new FrameworkPropertyMetadata(false, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault | FrameworkPropertyMetadataOptions.Inherits));
public static bool GetTest(DependencyObject obj)
{
return (bool)obj.GetValue(TestProperty);
}
public static void SetTest(DependencyObject obj, bool value)
{
obj.SetValue(TestProperty, value);
}
}
The XAML Code:
<Window ...>
<StackPanel local:Attached.Test="true" x:Name="f">
<CheckBox local:Attached.Test="true" IsChecked="{Binding (local:Attached.Test), Mode=TwoWay, RelativeSource={RelativeSource Self}}" />
<CheckBox local:Attached.Test="true" IsChecked="{Binding (local:Attached.Test), Mode=TwoWay}" />
</StackPanel>
</Window>
And the Binding Error:
System.Windows.Data Error: 40 : BindingExpression path error: '(local:Attached.Test)' property not found on 'object' ''StackPanel' (Name='f')'. BindingExpression:Path=(local:Attached.Test); DataItem='StackPanel' (Name='f'); target element is 'CheckBox' (Name=''); target property is 'IsChecked' (type 'Nullable`1')
Believe it or not, just add Path= and use parenthesis when binding to an attached property:
IsChecked="{Binding Path=(local:Attached.Test), Mode=TwoWay, RelativeSource={RelativeSource Self}}"
In addition, your call to RegisterAttached should pass in "Test" as the property name, not "TestProperty".
I'd have preferred to post this as a comment on Kent's answer but since I don't have enough rep to do so... just wanted to point out that as of WPF 4.5, adding Path= isn't necessary anymore. However the attached property name still needs to be wrapped with parentheses.
Putting a bracket works. I had to do automation id binding of a parent contentcontrol to a textblock in datatemplate. Automation Id is an attached property.
I put the property in brackets and binding worked.
AutomationProperties.AutomationId="{Binding RelativeSource={RelativeSource Mode=FindAncestor,AncestorType=ContentControl},Path=(AutomationProperties.AutomationId)}"
I searched and could not find an answer to my problem can someone help? I tried to bind a list of objects to a listbox. I have the correct number of items (I can still select them) but I could not see the texts.
My Xaml Code:
<ListBox x:Name="listbox_leave" BorderThickness="0">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel>
<TextBlock Text="{Binding leaveName}" />
<TextBlock Text="{Binding numberOfDays}" />
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
This is backend code:
ObservableCollection<Leave> leavelist = new ObservableCollection<Leave>();
leavelist = DbControl.loadLeaveDetails();
listbox_leave.ItemsSource = leavelist;
and my Leave Class:
class Leave
{
public string leaveName;
public string LeaveName
{
get { return leaveName; }
set { leaveName = value; }
}
public string numberOfDays;
public string NumberOfDays
{
get { return numberOfDays; }
set { numberOfDays = value; }
}
}
When I debug, the ObservableCollection list of Leave has all the correct data, my listbox display blank but it have the correct number of object Leave in it (I could select them) but there is no text displaying. And I have this msg:
System.Windows.Data Error: 40 : BindingExpression path error: 'leaveName' property not found on 'object' ''Leave' (HashCode=44930696)'. BindingExpression:Path=leaveName; DataItem='Leave' (HashCode=44930696); target element is 'TextBlock' (Name=''); target property is 'Text' (type 'String')
System.Windows.Data Error: 40 : BindingExpression path error: 'numberOfDays' property not found on 'object' ''Leave' (HashCode=44930696)'. BindingExpression:Path=numberOfDays; DataItem='Leave' (HashCode=44930696); target element is 'TextBlock' (Name=''); target property is 'Text' (type 'String')
System.Windows.Data Error: 40 : BindingExpression path error: 'leaveName' property not found on 'object' ''Leave' (HashCode=29274103)'. BindingExpression:Path=leaveName; DataItem='Leave' (HashCode=29274103); target element is 'TextBlock' (Name=''); target property is 'Text' (type 'String')
System.Windows.Data Error: 40 : BindingExpression path error: 'numberOfDays' property not found on 'object' ''Leave' (HashCode=29274103)'. BindingExpression:Path=numberOfDays; DataItem='Leave' (HashCode=29274103); target element is 'TextBlock' (Name=''); target property is 'Text' (type 'String')
Can Someone help??
You are trying to bind to the fields (leaveName and numberOfDays) instead of the properties (LeaveName and NumberOfDays), and this is not supported in WPF.
Change it to:
<ListBox x:Name="listbox_leave" BorderThickness="0">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel>
<TextBlock Text="{Binding LeaveName}" />
<TextBlock Text="{Binding NumberOfDays}" />
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
Also, you should probably go ahead and make the fields private (leaveName and numberOfDays) instead of public.
I am attempting to bind Image.Source in a DataTemplate to a System.Drawing.Image as discussed here: using XAML to bind to a System.Drawing.Image into a System.Windows.Image control
<UserControl.Resources>
<media:ImageConverter x:Key="imageConverter" />
<DataTemplate DataType="{x:Type data:GameTile}" >
<StackPanel Orientation="Vertical" Margin="5" Background="Transparent">
<Viewbox>
<TextBlock FontWeight="Bold" Text="{Binding PointValue}" TextAlignment="Center" FontSize="14" />
</Viewbox>
<Image Margin="0,5,0,0" Source="{Binding Path=Image.Image, Converter={StaticResource imageConverter}}" />
</StackPanel>
</DataTemplate>
</UserControl.Resources>
<Grid>
<loop:ListBox x:Name="listBox1"
ItemsSource="{Binding Path=GameModel.Game.GameTiles}"
ItemContainerStyle="{StaticResource GameTileContainerStyle}" Orientation="Vertical" />
</Grid>
The GameTile object has an Image (not a system.drawing.image) property that points to a Picture object which has an Image property of type System.Drawing.Image.
I am binding the ItemsSource on the ListBox to a GameTiles Collection on a Game object.
Objects
public class Game
{
public XPCollection<GameTile> GameTiles
{
get { return GetCollection<GameTile>("GameTiles"); }
}
}
public class GameTiles
{
Picture fImage;
public Picture Image
{
get { return fImage; }
set { SetPropertyValue<Picture>("Image", ref fImage, value); }
}
}
public class Picture
{
private FileData fFile;
public FileData File
{
get { return fFile; }
set
{
SetPropertyValue("File", ref fFile, value);
if (string.IsNullOrEmpty(fName))
{
fName = (value == null ? string.Empty : value.FileName);
}
fImage = null;
}
}
Image fImage;
public System.Drawing.Image Image
{
get
{
if (fImage == null)
{
try
{
MemoryStream stream = new MemoryStream();
fFile.SaveToStream(stream);
stream.Position = 0;
fImage = Image.FromStream(stream);
}
catch
{
//TODO: log exception
}
}
return fImage;
}
//set { SetPropertyValue<Image>("Image", ref fImage, value); }
}
}
The images are not showing up in the ListBoxItems, but any other property that I bind to in the DataTemplate will show up. It may be worth noting that I am using Devexpress Xpo as an ORM. Also the classes represented above do implement INotifyPropertyChanged.
Any thoughts on what I may be missing?
EDIT: Forgot to mention that I have implemented a value converter as mentioned in the post that I linked to above. However, if I put a breakpoint in the converter method, it is never called.
EDIT: Added the fFile property to the code above.
I can set an Image.Source to the GameTile.Image.Image property through c#(by converting it to BitmapImage), and have it work as expected, but I'm not sure how to accomplish that with a DataTemplate through c#. I would prefer to set the binding in XAML, but would settle for a c# workaround with a DataTemplate (or something else that would work). I am pretty confident that the issue is not with the GameTile.Image property pulling image from the database because if I manually set the source on an Image.Source in c#, the image is there. It simply isn't working in the DataTemplate.
Edit: Determined the issue to be related to properties that are not directly on the DataType that I am binding to for example with and GameTile has a (int)PointValue property, a (Picture object)Image property, and a (Prize object)Prize property.
If I bind to
<TextBlock Text="{Binding PointValue}" />
it works as expected.
But if I bind to
<TextBlock Text="{Binding Prize.Name}" />
it does not work.
And If I bind to
<Image Margin="0,5,0,0" Source="{Binding Image.BitmapImage}" />
it fails also. The following graphic shows the error that is being thrown by the binding.
BindingExpression path error: 'Name' property not found on 'object'
''XPCollection' (Hash=...)'. BindingExpression:Path=Prize.Name;
DataItem=GameTile' (HashCode=...); target element is
'TextBlock'(Name=''); target property is 'Text' (type 'String')
Thanks,
Eric
Found the solution.
Had to change this:
<TextBlock Text="{Binding Prize.Name}" />
to this:
<TextBlock Text="{Binding Prize!.Name}" />
The only difference is the exclamation point(!).
This also worked for the image property.
<Image Margin="0,5,0,0" Source="{Binding Path=Image!.Image, Converter={StaticResource imageConverter}}" />
I try to use binding with an attached property. But can't get it working.
public class Attached
{
public static DependencyProperty TestProperty =
DependencyProperty.RegisterAttached("TestProperty", typeof(bool), typeof(Attached),
new FrameworkPropertyMetadata(false, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault | FrameworkPropertyMetadataOptions.Inherits));
public static bool GetTest(DependencyObject obj)
{
return (bool)obj.GetValue(TestProperty);
}
public static void SetTest(DependencyObject obj, bool value)
{
obj.SetValue(TestProperty, value);
}
}
The XAML Code:
<Window ...>
<StackPanel local:Attached.Test="true" x:Name="f">
<CheckBox local:Attached.Test="true" IsChecked="{Binding (local:Attached.Test), Mode=TwoWay, RelativeSource={RelativeSource Self}}" />
<CheckBox local:Attached.Test="true" IsChecked="{Binding (local:Attached.Test), Mode=TwoWay}" />
</StackPanel>
</Window>
And the Binding Error:
System.Windows.Data Error: 40 : BindingExpression path error: '(local:Attached.Test)' property not found on 'object' ''StackPanel' (Name='f')'. BindingExpression:Path=(local:Attached.Test); DataItem='StackPanel' (Name='f'); target element is 'CheckBox' (Name=''); target property is 'IsChecked' (type 'Nullable`1')
Believe it or not, just add Path= and use parenthesis when binding to an attached property:
IsChecked="{Binding Path=(local:Attached.Test), Mode=TwoWay, RelativeSource={RelativeSource Self}}"
In addition, your call to RegisterAttached should pass in "Test" as the property name, not "TestProperty".
I'd have preferred to post this as a comment on Kent's answer but since I don't have enough rep to do so... just wanted to point out that as of WPF 4.5, adding Path= isn't necessary anymore. However the attached property name still needs to be wrapped with parentheses.
Putting a bracket works. I had to do automation id binding of a parent contentcontrol to a textblock in datatemplate. Automation Id is an attached property.
I put the property in brackets and binding worked.
AutomationProperties.AutomationId="{Binding RelativeSource={RelativeSource Mode=FindAncestor,AncestorType=ContentControl},Path=(AutomationProperties.AutomationId)}"
For some reason when I bind a ComboBox to a list of POCOs, the property which is bound to SelectedValue is set twice:
With value = POCO.ToString()
With value = POCO.Key property, which is the intended behaviour
I have the following ComboBox that is bound to properties in my ViewModel:
<ComboBox ItemsSource="{Binding Path=AllowedClassifications}"
DisplayMemberPath="Value"
SelectedValue="{Binding Path=TargetGroup.Classification}"
SelectedValuePath="Key" />
The properties in ViewModel are defined as:
//ICollection is implemented by ObservableCollection<T>
//DataBaseFieldValue has two public properties: string Key, string Value
public ICollection<DatabaseFieldValue> AllowedClassifications
{
get { return _allowedClassifications; }
private set { _allowedClassifications = value; }
}
public Model.TargetGroup TargetGroup
{
get { return _targetGroup; }
private set { _targetGroup = value; OnPropertyChanged("TargetGroup"); }
}
TargetGroup.Classification is defined as:
public string Classification
{
get { return _classification; }
set
{
System.Diagnostics.Debug.WriteLine("Classification: " + value);
_classification = value;
OnPropertyChanged("Classification");
}
}
Debug output:
Classification:
MyNamespace.DatabaseFieldValue
Classification: 2
What's happening here? Am I doing this completely wrong?
Everything looks OK in your code, except for the fact that according to your XAML the property which is bound to SelectedValue should be set to the POCO.Key value rather than POCO.Value (as you wrote you expected). I have just created a test project with similar setup and everything works.
Alternatively, you could try using SelectedItem property of combobox in combination with ItemTemplate:
<ComboBox ItemsSource="{Binding Path=AllowedClassifications}"
SelectedItem="{Binding Path=TargetGroup.Classification}" >
<ComboBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Value}"/>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
In this case the TargetGroup.Classification property must be of type DatabaseFieldValue.