Generic.xaml binding to a objectDataProvider - c#

I don't exactly know what the problem is since I am almost sure I did what other posts told me to do. I have bound a observablecollection of strings to a comboBox before so it should work.
dataClass:
namespace UIBlocksLib.Data_VM__classes
{
public class BlockController : INotify, IStatementCollection
{
private List<UIStackBlock> _mUIStackBlocks = new List<UIStackBlock>();
public ObservableCollection<string> _mUIVariables = new ObservableCollection<string>() { "VariableA", "VariableB", "VariableC" };
public event PropertyChangedEventHandler PropertyChanged;
public ObservableCollection<string> getVariables
{
get
{
return _mUIVariables;
}
set
{
_mUIVariables = value;
onPropertyChanged("_mUIVariables");
}
}
public BlockController()
{
}
public void addVariable(string aVariableName)
{
_mUIVariables.Add(aVariableName);
onPropertyChanged("_mUIVariables");
}
public void addUIStackBlock(UIStackBlock aUIStackBlock)
{
throw new NotImplementedException();
}
public void onPropertyChanged(string aPropertyName)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(aPropertyName));
}
public void removeStackBlockByIndex(int aIndex)
{
throw new NotImplementedException();
}
}
}
my objectDataProvider in my generic.xaml
<ObjectDataProvider x:Key="dataObject"
ObjectType="{x:Type dataClass:BlockController}"
MethodName="getVariables">
</ObjectDataProvider>
and my style which is bound to my class
<Style TargetType="{x:Type local:UISet}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type local:UISet}">
<Grid>
<Rectangle MouseLeftButtonDown="MouseLeftButtonDown" Height="{TemplateBinding Height}" Width="{TemplateBinding Width}" Fill="#AD2B27" ClipToBounds="True"/>
<ComboBox DataContext="{staticResource dataObject}" Background="#FFEE4A4A" x:Name="comboBox" MinHeight="30" MinWidth="70" Margin="5, 23" ItemsSource="{Binding}"/>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
also tried making the context static/ not using a context/ using the name of the variable. the compiler does recognize the getVariables when looking through the suggestions.

In your Code getVariables is not a method but a property. This way it should work:
<ObjectDataProvider x:Key="dataObject"
ObjectType="{x:Type dataClass:BlockController}"
MethodName="GetVariables">
</ObjectDataProvider>
public ObservableCollection<string> GetVariables()
{
return getVariables;
}

Related

How to take care of two DataContexts

I've merged two programs, and now there are two DataContexts in my code. One is DataContext = this; and the other is DataContext = _uiData;. I'd like to keep the former, because there are many bindings relying on this DataContext (not shown in my code, though). For the latter, I changed it to textBox.Text = _uiData.ToString();, but it doesn't show anything. How would you take care of this kind of situation?
Here is my code:
MainWindow.xaml.cs
using Both_ListBox_n_TextBox_Binding.ViewModel;
using System.Collections.ObjectModel;
using System.Windows;
namespace Both_ListBox_n_TextBox_Binding
{
public partial class MainWindow : Window
{
public ObservableCollection<GraphViewModel> RightListBoxItems { get; }
= new ObservableCollection<GraphViewModel>();
private UISimpleData _uiData = new UISimpleData();
public MainWindow()
{
InitializeComponent();
RightListBoxItems.Add(new GraphViewModel("T1"));
RightListBoxItems.Add(new GraphViewModel("T2"));
RightListBoxItems.Add(new GraphViewModel("T3"));
DataContext = _uiData; // How can I show this?
//textBox.Text = _uiData.ToString(); // This doesn't show anything
DataContext = this; // I'd like to KEEP this
//RightListBox.ItemsSource = RightListBoxItems; // Works, but not for this time
}
}
}
MainWindow.xaml
<Window x:Class="Both_ListBox_n_TextBox_Binding.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:Both_ListBox_n_TextBox_Binding"
mc:Ignorable="d"
Title="MainWindow" Height="253.5" Width="297.5">
<Grid>
<ListBox x:Name="RightListBox"
ItemsSource="{Binding RightListBoxItems}" DisplayMemberPath="Text"
SelectionMode="Extended" Margin="20,20,20,100"/>
<TextBox Margin="100,0,0,40" HorizontalAlignment="Left" VerticalAlignment="Bottom" Height="22" Width="100"
Text="{Binding DoubleField, UpdateSourceTrigger=PropertyChanged, ValidatesOnDataErrors=True}">
<TextBox.Style>
<Style TargetType="{x:Type TextBox}">
<Style.Triggers>
<Trigger Property="Validation.HasError" Value="true">
<Setter Property="ToolTip" Value="{Binding (Validation.Errors)[0].ErrorContent,
RelativeSource={RelativeSource Self}}"/>
</Trigger>
</Style.Triggers>
</Style>
</TextBox.Style>
</TextBox>
</Grid>
</Window>
ViewModel/UISimpleData.cs
using System;
using System.ComponentModel;
namespace Both_ListBox_n_TextBox_Binding.ViewModel
{
public class UISimpleData : INotifyPropertyChanged, IDataErrorInfo
{
private double _doubleField = 5.5;
public double DoubleField
{
get
{
return _doubleField;
}
set
{
if (_doubleField == value)
return;
_doubleField = value;
RaisePropertyChanged("DoubleField");
}
}
public string this[string propertyName]
{
get
{
string validationResult = null;
switch (propertyName)
{
case "DoubleField":
{
if (DoubleField < 0.0 || DoubleField > 10.0)
validationResult = "DoubleField is out of range";
break;
}
default:
throw new ApplicationException("Unknown Property being validated on UIData");
}
return validationResult;
}
}
public string Error { get { return "Not Implemented"; } }
public event PropertyChangedEventHandler PropertyChanged;
protected void RaisePropertyChanged(string property)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(property));
}
}
}
ViewModel/GraphViewModel.cs
namespace Both_ListBox_n_TextBox_Binding.ViewModel
{
public class GraphViewModel
{
public string Text { get; }
public GraphViewModel(string text) => Text = text;
}
}
Thank you in advance.

textBlock isn't updated (RaisePropertyChanged isn't working?)

My program has two windows. The content of TextBox on MainWindow should change the content of a TextBlock on CalcWindow. However, the TextBlock doesn't change, even after the TextBox is changed.
I added "RaisePropertyChanged" to UISimpleData. So, the content of TextBox is correctly changed. But, it doesn't change the TextBlock on CalcWindow.
MainWindow.xaml
<Window x:Class="DoubleToTextBlockBinding.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="DataUpdate" Height="220.276" Width="400">
<Grid Height="190" Margin="0,0,-0.667,0" VerticalAlignment="Top">
<Label Content="Target Value" HorizontalAlignment="Right" Margin="0,0,112,142" VerticalAlignment="Bottom" Width="78"/>
<TextBox Margin="0,0,24,142" HorizontalAlignment="Right" VerticalAlignment="Bottom" Height="22" Width="60"
Text="{Binding DoubleField, UpdateSourceTrigger=PropertyChanged, ValidatesOnDataErrors=True}">
<TextBox.Style>
<Style TargetType="{x:Type TextBox}">
<Style.Triggers>
<Trigger Property="Validation.HasError" Value="true">
<Setter Property="ToolTip" Value="{Binding (Validation.Errors)[0].ErrorContent,
RelativeSource={RelativeSource Self}}"/>
</Trigger>
</Style.Triggers>
</Style>
</TextBox.Style>
</TextBox>
<TextBlock Text="{Binding DoubleField}" HorizontalAlignment="Right" VerticalAlignment="Bottom"
Margin="0,0,25,115" Height="22" Width="60"/>
<Button Name="ConfirmButton" Content="Confirm" Margin="85,0,25.666,58" HorizontalAlignment="Right" VerticalAlignment="Bottom" Click="ConfirmButton_Click"/>
</Grid>
</Window>
MainWindow.xaml.cs
using DoubleToTextBlockBinding.ViewModels;
using DoubleToTextBlockBinding.Views;
using System.Windows;
namespace DoubleToTextBlockBinding
{
public partial class MainWindow : Window
{
private UISimpleData _uiData = new UISimpleData();
public MainWindow()
{
InitializeComponent();
DataContext = _uiData;
}
private void ConfirmButton_Click(object sender, RoutedEventArgs e)
{
new CalcWindow().Show();
}
}
}
App.xaml.cs
using System.Windows;
namespace DoubleToTextBlockBinding
{
public partial class App : Application
{
public App()
{
System.Windows.FrameworkCompatibilityPreferences
.KeepTextBoxDisplaySynchronizedWithTextProperty = false;
}
}
}
Views/CalcWindow.xaml
<Window xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:viewModels="clr-namespace:DoubleToTextBlockBinding.ViewModels"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
x:Class="DoubleToTextBlockBinding.Views.CalcWindow"
Title="Bound Window" Width="400" Height="212">
<Grid>
<TextBlock Text="{Binding DoubleField}" x:Name="textBox" Width="104"
Margin="148,84,0,0" HorizontalAlignment="Left" VerticalAlignment="Top">
<TextBlock.DataContext>
<viewModels:UISimpleData/>
</TextBlock.DataContext>
</TextBlock>
</Grid>
</Window>
Views/CalcWindow.xaml.cs
using System.Windows;
using DoubleToTextBlockBinding.ViewModels;
namespace DoubleToTextBlockBinding.Views
{
public partial class CalcWindow : Window
{
private UISimpleData _uiData = new UISimpleData();
public CalcWindow()
{
InitializeComponent();
this.DataContext = _uiData;
}
}
}
ViewModels/UISimpleData.cs
using System;
using System.ComponentModel;
using System.Windows;
namespace DoubleToTextBlockBinding.ViewModels
{
public class UISimpleData : INotifyPropertyChanged, IDataErrorInfo
{
private double _doubleField = 2.0;
public double DoubleField
{
get
{
return _doubleField;
}
set
{
if (_doubleField == value)
return;
_doubleField = value;
RaisePropertyChanged("DoubleField");
}
}
public string this[string propertyName]
{
get
{
string validationResult = null;
switch (propertyName)
{
case "DoubleField":
{
if (DoubleField < 0 || DoubleField > 5)
validationResult = "DoubleField is out of range";
break;
}
default:
throw new ApplicationException("Unknown Property being validated on UIData");
}
return validationResult;
}
}
public string Error { get { return "Not Implemented"; } }
public event PropertyChangedEventHandler PropertyChanged;
protected void RaisePropertyChanged(string property)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(property));
//MessageBox.Show("Changed to " + DoubleField);
}
}
}
Expected behavior:
Change the content of TextBox to "3".
(You will see the TextBlock on the same window changes to "3".)
Click on the "Confirm" button.
A new window appears. Check if the content of the TextBlock is "3".
(The actual result becomes "2" here.)
... This is the best I can do for now. Please help me. Thank you.
you are creating new instances of your viewmodel in both classes. If you change something in the viewmodel in MainWindow, you have to pass this model to the CalcWindow.
my guess is that you should write the constructor for CalcWindow like this:
public CalcWindow(UISimpleData yourViewModelFromMainWindow)
{
InitializeComponent();
this.DataContext = yourViewModelFromMainWindow;
}
and in the MainWindow, in method ConfirmButton_Click
private void ConfirmButton_Click(object sender, RoutedEventArgs e)
{
new CalcWindow(this.DataContext as UISimpleData).Show();
}
I hope this is helpful. If it's not, do not hesitate to ask.

How to Bind ItemsSource of ComboBox in ControlTemplate?

Situation
I have to change the content of the Flyout Item in GridView. So I am creating ControlTemplate in Page.Resources and setting it in ContentControl which is inside Flyout.
Problem
I have a ComboBox in ControlTemplate. Now I want to set the ItemsSource of ComboBox to List<string> (_easingType) which is declared in MainPage
Question
How to Bind/Set ItemsSource of ComboBox in ControlTemplate?
Code
I have removed the unnecessary parts of the code
XAML
<Page.Resources>
<ControlTemplate x:Key="BlurEditFlyout">
....
<ComboBox ItemsSource="{Bind it to the _esaingType}" />
....
<ControlTemplate x:Key="BlurEditFlyout">
</Page.Resources>
<GridView ItemsSource="{x:Bind _items}">
<GridView.ItemTemplate>
<DataTemplate x:DataType="local:MethodData">
<StackPanel>
....
<Button Visibility="{x:Bind EditButtonVisibility}">
<Button.Flyout>
<Flyout>
<Flyout.FlyoutPresenterStyle>
<Style TargetType="FlyoutPresenter">
<Setter Property="ScrollViewer.HorizontalScrollMode" Value="Disabled" />
<Setter Property="ScrollViewer.HorizontalScrollBarVisibility" Value="Disabled" />
</Style>
</Flyout.FlyoutPresenterStyle>
<ContentControl Template="{x:Bind FlyoutTemplate}"/>
</Flyout>
</Button.Flyout>
<SymbolIcon Symbol="Edit"/>
</Button>
....
</StackPanel>
</DataTemplate>
</GridView.ItemTemplate>
</GridView>
Code Behind
public sealed partial class MainPage : Page
{
ObservableCollection<MethodData> _items = new ObservableCollection<MethodData>();
List<string> _easingType = new List<string>(Enum.GetNames(typeof(EasingType)).ToArray());
Dictionary<MethodName, ControlTemplate> _buttonFlyoutDictionary = new Dictionary<MethodName, ControlTemplate>();
public MainPage()
{
this.InitializeComponent();
LoadFlyoutResources();
_items.Add(GetMethodData(MethodName.Blur));
}
private void LoadFlyoutResources()
{
_buttonFlyoutDictionary.Add(MethodName.Blur, (ControlTemplate)Resources["BlurEditFlyout"]);
.....
}
private MethodData GetMethodData(MethodName methodName)
{
_buttonFlyoutDictionary.TryGetValue(methodName, out ControlTemplate flyoutTemplate);
return new MethodData(methodName, flyoutTemplate);
}
}
public class MethodData
{
public string Name { get; set; }
public ControlTemplate FlyoutTemplate { get; set; }
public Visibility EditButtonVisibility { get; set; }
public MethodData(MethodName name, ControlTemplate flyoutTemplate)
{
Name = name.ToString();
FlyoutTemplate = flyoutTemplate;
EditButtonVisibility = (name == MethodName.Then) ? Visibility.Collapsed : Visibility.Visible;
}
}
public enum MethodName
{
Blur,
....
}
Full Code
AnimationSetSamplePage.zip
Your DataContext in the Flyout control is actually each item in "_items". You'll want to create a DataContext proxy to get to your Page's DataContext. You can follow either of these two links to create the proxy.
https://weblogs.asp.net/dwahlin/creating-a-silverlight-datacontext-proxy-to-simplify-data-binding-in-nested-controls
http://www.thomaslevesque.com/2011/03/21/wpf-how-to-bind-to-data-when-the-datacontext-is-not-inherited/
The gist of it is you'll want to create a proxy that you can reference as a static resource. Following the first link, you'll do something like this:
public class DataContextProxy : FrameworkElement
{
public DataContextProxy()
{
this.Loaded += new RoutedEventHandler(DataContextProxy_Loaded);
}
void DataContextProxy_Loaded(object sender, RoutedEventArgs e)
{
Binding binding = new Binding();
if (!String.IsNullOrEmpty(BindingPropertyName))
{
binding.Path = new PropertyPath(BindingPropertyName);
}
binding.Source = this.DataContext;
binding.Mode = BindingMode;
this.SetBinding(DataContextProxy.DataSourceProperty, binding);
}
public Object DataSource
{
get { return (Object)GetValue(DataSourceProperty); }
set { SetValue(DataSourceProperty, value); }
}
public static readonly DependencyProperty DataSourceProperty =
DependencyProperty.Register("DataSource", typeof(Object), typeof(DataContextProxy), null);
public string BindingPropertyName { get; set; }
public BindingMode BindingMode { get; set; }
}
You should use public access modifier for _easingType
public List<string> _easingType = new List<string>(Enum.GetNames(typeof(EasingType)).ToArray());
In MainPage.xaml
<Page.Resources>
<local:DataContextProxy x:Key="DataContextProxy" />
<ControlTemplate x:Key="BlurEditFlyout">
....
<ComboBox ItemsSource="{Binding Source={StaticResource DataContextProxy}, Path=DataSource._easingType}" />
....
<ControlTemplate x:Key="BlurEditFlyout">
</Page.Resources>
...
How to Bind/Set ItemsSource of ComboBox in ControlTemplate?
I'm not sure if you have deep reasons for asking this question, but directly to reply this question, we could just set the string list _esaingType as the value of DataContext property and binding it. For example:
XAML
<Page.Resources>
<ControlTemplate TargetType="FlyoutPresenter" x:Key="BlurEditFlyout" >
...
<ComboBox ItemsSource="{Binding}" />
...
</ControlTemplate>
</Page.Resources>
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<Button>
<Button.Flyout>
<Flyout>
<Flyout.FlyoutPresenterStyle>
<Style TargetType="FlyoutPresenter">
<Setter Property="ScrollViewer.HorizontalScrollMode" Value="Disabled" />
<Setter Property="ScrollViewer.HorizontalScrollBarVisibility" Value="Disabled" />
<Setter Property="Template" Value="{StaticResource BlurEditFlyout}">
</Setter>
</Style>
</Flyout.FlyoutPresenterStyle>
<!--<ContentControl Template="{StaticResource BlurEditFlyout}"/>-->
</Flyout>
</Button.Flyout>
<SymbolIcon Symbol="Edit"/>
</Button>
</Grid>
Code behind
List<string> _easingType = new List<string>();
public MainPage()
{
this.InitializeComponent();
_easingType.Add("test2");
_easingType.Add("test1");
this.DataContext = _easingType;
}
Any concerns on this way or any issues when using this on your side please kindly tell me I will follow up in time. More details please reference Data binding in depth.

WPF list does not refresh

I'm trying to create a new page in a WPF application. It is an "old" project, discovering the project and the language (I know C/C++, not C# nor XAML). In that page, I have to put a list of "rapport". But I can't refresh that list with new items, nor refresh the items.
I can't put that list in XAML, I have to use a C# variable. I try a lot of things, like List, observable list, refresh, binding, trigger event from expander, so a lot of code may be useless / unreachable.
And for a precision, piramidHandler.GetListParamReport(); has to be in the piramid class.
Thank's a lot for your help, pardon my English, still not my native language, but I try to make my best.
Rapports.xaml :
<Expander Header="{x:Static resx:StringResources.Lib_TabRapports}" VerticalAlignment="Top" Grid.Row="2" IsExpanded="False" IsEnabled="True" Expanded="ExpanderRapport">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<!--First row for controls-->
<Grid Grid.Row="0">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="650" />
<ColumnDefinition Width="650" />
</Grid.ColumnDefinitions>
</Grid>
<DataGrid ItemsSource="{Binding ListRapports}" AutoGenerateColumns="False" CanUserAddRows="False" CanUserResizeColumns="False" Name="RapportGrid"
CanUserReorderColumns="False" Style="{StaticResource AzureDataGrid}" Grid.Row="1" FrozenColumnCount="2"
ScrollViewer.CanContentScroll="True" ScrollViewer.VerticalScrollBarVisibility="Visible" ScrollViewer.HorizontalScrollBarVisibility="Auto">
<DataGrid.Resources>
<Style x:Key="AlignBottomColumnHeader" BasedOn="{StaticResource AzureDataGridColumnHeader}" TargetType="DataGridColumnHeader">
<Setter Property="VerticalContentAlignment" Value="Bottom" />
</Style>
<Style x:Key="RotatedColumnHeader" BasedOn="{StaticResource AzureDataGridColumnHeader}" TargetType="DataGridColumnHeader">
<Setter Property="HorizontalContentAlignment" Value="Stretch" />
<Setter Property="VerticalContentAlignment" Value="Stretch" />
<Setter Property="Margin" Value="-4,0,0,0" />
<Setter Property="BorderBrush" Value="White" />
<Setter Property="LayoutTransform">
<Setter.Value>
<RotateTransform Angle="-90" />
</Setter.Value>
</Setter>
</Style>
</DataGrid.Resources>
RapportViewModel.cs :
public sealed partial class Piramid
{
internal ObservableCollection<ViewModels.Rapport> listRapports;
public string GetRapportsList()
{
return piramidHandler.GetListParamReport();
}
public ObservableCollection<ViewModels.Rapport> ReloadRapportsLists()
{
var uniqueListRapport = new HashSet<ViewModels.Rapport>();
string gabaritsXML = GetRapportsList();
// [...] Fill uniqueListRapport
return new ObservableCollection<ViewModels.Rapport>(uniqueListRapport.ToList());
}
}
public class TabRapport : ViewModelBase
{
#region Properties
public TabRapport()
{
}
public ObservableCollection<ViewModels.Rapport> ListRapports
{
get
{
ObservableCollection<Rapport> test;
test = Piramid.Instance.ReloadRapportsLists();
if (test.Count == 0)
{
ViewModels.Rapport current = new ViewModels.Rapport
{
IdRapport = 52,
NameRapport = "This is a try"
};
test.Add(current);
ViewModels.Rapport current2 = new ViewModels.Rapport
{
IdRapport = 54,
NameRapport = "Second try"
};
test.Add(current2);
}
return test;
}
}
#endregion Properties
#region commands
// Useless in the context
#endregion commands
}
public class Rapport : ViewModelBase
{
public Rapport()
{
idRapport = -1;
}
public Rapport(Rapport e)
{
idRapport = e.IdRapport;
nameRapport = e.NameRapport;
}
private int idRapport;
private string nameRapport;
public int IdRapport
{
get => idRapport;
set => idRapport = value;
}
public string NameRapport
{
get => nameRapport;
set => nameRapport = value;
}
}
Rapports.xaml.cs :
public partial class Rapports : UserControl, INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
OnTargetUpdated();
}
private void ExpanderRapport(object sender, RoutedEventArgs args)
{
OnTargetUpdated();
}
public Rapports()
{
InitializeComponent();
Thread.CurrentThread.CurrentCulture = App.AppCultureInfo;
Thread.CurrentThread.CurrentUICulture = App.AppCultureInfo;
}
public void OnTargetUpdated()
{
RapportsRoot.Reload();
RapportGrid.Items.Refresh();
}
public void OnTargetUpdated(object sender, System.EventArgs e)
{
RapportsRoot.Reload();
RapportGrid.Items.Refresh();
}
}
EDIT :
DataContext : DataContext="{Binding Source={StaticResource Locator}, Path=OngletRapport}">
Reference to :
public TabRapport OngletRapport
{
get
{
return ServiceLocator.Current.GetInstance<TabRapport>();
}
}
EDIT 2 : Added code in the RapportViewModel.cs. My current try just show "This is a try" and "second try" as a list, not able to change it after setting data in listRapport
Okay, I finally find the answer.
I need to use ObservableObject.RaisePropertyChanged("ListRapports"); at the end of all background traitement, forcing the listRapports to refresh all data and items number.
Thank's a lot for all your help :)

defining bindings for elements in a collection in xaml wont work

i have a simple Control with a collection of elements. I want to add elements in xaml and bind to the element.
However when i bind to Bar.Value in xaml it never works. Minimal example:
[ContentProperty("Bars")]
public class FooControl : Control
{
private ObservableCollection<IBar> _bars = new ObservableCollection<IBar>();
public ObservableCollection<IBar> Bars { get { return _bars; } }
}
public interface IBar
{
string Value { get; }
}
public class Bar : DependencyObject, IBar
{
public static readonly DependencyProperty ValueProperty = DependencyProperty.Register("Value", typeof(string), typeof(Bar), new PropertyMetadata("<not set>"));
public string Value
{
get { return (string)GetValue(ValueProperty); }
set { SetValue(ValueProperty, value); }
}
}
<Window x:Class="WpfTestApplication.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:this="clr-namespace:WpfTestApplication"
xmlns:sys="clr-namespace:System;assembly=mscorlib"
Title="MainWindow" Height="200" Width="1000">
<Window.Resources>
<Style TargetType="this:FooControl">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="this:FooControl">
<ItemsControl ItemsSource="{Binding Bars, RelativeSource={RelativeSource TemplatedParent}}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Value}"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Window.Resources>
<Window.DataContext>
<sys:String>from DataContext</sys:String>
</Window.DataContext>
<Grid>
<this:FooControl>
<this:Bar Value="directly set"/>
<this:Bar Value="{Binding Source=from_binding_source}"/>
<this:Bar Value="{Binding}"/>
<this:Bar Value="{Binding Text, ElementName=SomeTextBlock}"/>
</this:FooControl>
<TextBlock Text="from TextBox" Name="SomeTextBlock" Visibility="Collapsed"/>
</Grid>
</Window>
output
directly set
from_binding_source
"<not set>"
"<not set>"
debug output
System.Windows.Data Error: 2 : Cannot find governing FrameworkElement or FrameworkContentElement for target element. BindingExpression:Path=Text; DataItem=null; target element is 'Bar' (HashCode=26568931); target property is 'Value' (type 'String')
Any suggestion how to get it to work?
My current workaround is to define the bindings in code but this is a lot more code and looking at the xaml its not obvious which bindings do exists. (see my answer for workaround code)
dont know if this helps but
[ContentProperty("Bars")]
public class FooControl : Control
{
private ObservableCollection<object> _bars = new ObservableCollection<object>();
public ObservableCollection<object> Bars { get { return _bars; } }
}
xaml
<this:FooControl>
<this:Bar Value="directly set"/>
<this:Bar Value="{Binding Source=from_binding_source}"/>
<TextBlock Text="{Binding}"/>
<TextBlock Text="{Binding Text, ElementName=SomeTextBlock}"/>
</this:FooControl>
you get your desired result
my workaround so far
<this:Bar x:Name="bar3" />
<this:Bar x:Name="bar4" />
void MainWindow_Loaded(object sender, RoutedEventArgs e)
{
var bar3 = (DependencyObject)FindName("bar3");
BindingOperations.SetBinding(bar3, Bar.ValueProperty, new Binding("DataContext") { Source = this });
var bar4 = (DependencyObject)FindName("bar4");
BindingOperations.SetBinding(bar4, Bar.ValueProperty, new Binding("Text") { Source = FindName("SomeTextBlock") });
}

Categories

Resources