I'm using wpf and c# with the Third party DevExpress Libraries. I'm having a problem with DXCharts. I've tried a few different things to clear or update the chart to no avail. I'm Data-binding to a Data-table(built on the fly) with a dependency property for the Data-source.
The dependency properties for the chart do not seem to be overridden when new data is set to the backing property. This gives me overlaying points on the chart. As you can see in the examples below.
1st Set of Data
2nd Set of Data
I also tried creating new instances of the Chart control and its still showing the old binded dependency properties. The DXchart usercontrol is embedded into a Content Control. I bind the Chart via a content property. All of this is nested under a DevExpress tab control.
Here is some of the code below:
Dependency Properties
public static readonly DependencyProperty DataTableChartProperty = DependencyProperty.Register
("DataTableChart", typeof(DataTable), typeof(MainWindowViewModel));
public static readonly DependencyProperty ContentElementProperty = DependencyProperty.Register
("ContentElement", typeof(FrameworkElement), typeof(MainWindowViewModel));
Backing Properties
public DataTable DataTableChart
{
get { return (DataTable)this.GetValue(DataTableChartProperty); }
set { this.SetValue(DataTableChartProperty, value); }
public FrameworkElement ContentElement
{
get { return (FrameworkElement)this.GetValue(ContentElementProperty); }
set { this.SetValue(ContentElementProperty, value); }
}
UserControl
<UserControl x:Class="Reporting_DIMS.UI.ChartControl"
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:dx="http://schemas.devexpress.com/winfx/2008/xaml/core"
xmlns:dxb="http://schemas.devexpress.com/winfx/2008/xaml/bars"
xmlns:dxc="http://schemas.devexpress.com/winfx/2008/xaml/charts"
xmlns:dxg="http://schemas.devexpress.com/winfx/2008/xaml/grid"
xmlns:dxe="http://schemas.devexpress.com/winfx/2008/xaml/editors"
mc:Ignorable="d"
d:DesignHeight="700" d:DesignWidth="1100">
<Grid>
<Border Padding="3">
<dxc:ChartControl Margin="0" Name="chartControl" DataSource="{Binding DataTableChart}">
<dxc:ChartControl.Diagram>
<dxc:XYDiagram2D SeriesDataMember="DIMS User">
<dxc:XYDiagram2D.SeriesTemplate>
<dxc:BarSideBySideSeries2D ValueDataMember="Count" ArgumentDataMember="Entry DateTime" />
</dxc:XYDiagram2D.SeriesTemplate>
</dxc:XYDiagram2D>
</dxc:ChartControl.Diagram>
<dxc:ChartControl.Legend>
<dxc:Legend x:Name="legend"/>
</dxc:ChartControl.Legend>
</dxc:ChartControl>
</Border>
</Grid>
Small MainWindow Portion
<dx:DXTabItem Header="Log Charts" Name="dXTabItem2">
<ContentControl x:Name="contentControl" Content="{Binding ContentElement}"/>
</dx:DXTabItem>
If anyone has any ideas, I would greatly appreciate it. Thanks in Advance!
rreeves is correct. A simple workaround for me was to wrap my DXChartControl in a 2nd ContentControl. Instead of binding directly to the ChartControl, let an inner ContentControl hand the binding to the ChartControl, that way,when the content is changed, the ContentControl generates a new instance via the ContentTemplate.
<DataTemplate x:Key="chartTemplate">
<dex:ChartControl DataSource="{Binding}" DataContextChanged="chartControl_DataContextChanged_1"/>
</DataTemplate>
<ContentControl Grid.Row="1" ContentTemplate="{StaticResource ResourceKey=chartTemplate}" Content="{Binding 'YOUR ITEMSSOURCE'}"/>
Then you can rebuil the Chart in the DataContextChanged.
Hope this helps!
I ended up removing the old charting object and creating a new one. This is the intended behavior of the Charts per DevExpress.
Related
Goal:
I want to achieve something similar to the GroupBox control.
I want to have some design and controls wrapping around a child element which I specify in XAML:
Current code:
<GroupBox Header="Filter">
<local:Filterview></local:Filterview>
</GroupBox>
Goal:
<objectViews:ContractableGroupBox Header="TEST">
<local:Filterview></local:Filterview>
</objectViews:ContractableGroupBox>
Current Situation / Issue:
My custom "groupbox" works as far as adding it to the Form and setting the header but it does not work properly when adding the child element Filterview.
Working (But no content):
<objectViews:ContractableGroupBox Header="TEST">
</objectViews:ContractableGroupBox>
Bugs out (content is there but wrapping not):
<objectViews:ContractableGroupBox Header="TEST">
<local:Filterview></local:Filterview>
</objectViews:ContractableGroupBox>
Code Behind:
This is the XAML of ContractableGroupBox:
<UserControl x:Class="SoundStudio.Views.ObjectViews.ContractableGroupBox"
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:SoundStudio.Views.ObjectViews"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800">
<Grid>
<Border BorderBrush="#FF303030" Background="#FF646464" CornerRadius="8,8,3,3" >
<Expander x:Name="ExpanderContent" Header="{Binding Header}" IsExpanded="True">
</Expander>
</Border>
</Grid>
</UserControl>
Note, I want to specify the child element in the parent UserControl, but it should be displayed as if in the Expander node such as:
<Expander x:Name="ExpanderContent" Header="{Binding Header}" IsExpanded="True">
<local:Filterview></local:Filterview>
</Expander>
This is the current ContractableGroupBox.cs
using System.Windows.Controls;
namespace SoundStudio.Views.ObjectViews
{
/// <summary>
/// Interaction logic for ContractableGroupBox.xaml
/// </summary>
public partial class ContractableGroupBox : UserControl
{
public ContractableGroupBox()
{
InitializeComponent();
this.DataContext = this;
}
public string Header { get; set;}
}
}
What you see ist that the following XAML overrides the content of your UserControl, which includes the Grid and the Expander itself and that is why the header is seemingly lost.
<objectViews:ContractableGroupBox Header="TEST">
<local:Filterview></local:Filterview>
</objectViews:ContractableGroupBox>
As a general advise, do not ever set the DataContext of a UserControl to itself, this will break data context inheritance and is bad practice. Regarding your issue, you should make Header a dependency property to enable data binding and add another dependency property for the content of the expander, e.g. ExpanderContent (Content already exists on UserControl).
[ContentProperty(nameof(ExpanderContent))]
public partial class ContractableGroupBox : UserControl
{
public static readonly DependencyProperty HeaderProperty = DependencyProperty.Register(
nameof(Header), typeof(string), typeof(ContractableGroupBox));
public static readonly DependencyProperty ExpanderContentProperty = DependencyProperty.Register(
nameof(ExpanderContent), typeof(object), typeof(ContractableGroupBox));
public ContractableGroupBox()
{
InitializeComponent();
}
public string Header
{
get => (string)GetValue(HeaderProperty);
set => SetValue(HeaderProperty, value);
}
public object ExpanderContent
{
get => GetValue(ExpanderContentProperty);
set => SetValue(ExpanderContentProperty, value);
}
}
The ContentProperty attribute at the top will make sure that anything you put inside this user control in XAML like below will be assigned to the ExpanderContent property instead of the Content property that the UserControl type already provides. If you do not do this, you have to assign your content manually to ExpanderContent, otherwise the actual content of the UserControl itself (your Grid, Expander, etc. will be overridden.
<objectViews:ContractableGroupBox Header="TEST">
<local:Filterview></local:Filterview>
</objectViews:ContractableGroupBox>
You have to change your user control XAML bindings using RelativeSource and AncestorType, so that they resolve the dependency properties Header and ExpanderContent on your control. Notice, that I renamed the Expander to Expander to avoid a naming collision with the dependency property ExpanderContent. Now that the bindings use the dependency properties, there is even no need for setting the DataContext.
<UserControl x:Class="SoundStudio.Views.ObjectViews.ContractableGroupBox"
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:SoundStudio.Views.ObjectViews"
mc:Ignorable="d" >
<Grid>
<Border BorderBrush="#FF303030" Background="#FF646464" CornerRadius="8,8,3,3" >
<Expander x:Name="Expander"
IsExpanded="True"
Header="{Binding Header, RelativeSource={RelativeSource AncestorType={x:Type UserControl}}}"
Content="{Binding ExpanderContent, RelativeSource={RelativeSource AncestorType={x:Type UserControl}}}"/>
</Border>
</Grid>
</UserControl>
However, if the only thing that you want to add to the Expander is a Border around it, then you do not have to create a separate UserControl. You could just create a custom control template for Expander by copying its default template and add a Border there.
I am very new to the WPF development. I am updating an existing application and there seems to be a MVVM framework implemented.
Now I have a user control(ChartView.xaml) which has a dependency property:
public partial class ChartView : UserControl, IDisposable
{
public static readonly DependencyProperty SceneProperty = DependencyProperty.Register(
"Scene",
typeof(IScene),
typeof(ChartView),
new FrameworkPropertyMetadata(
default(IScene),
FrameworkPropertyMetadataOptions.AffectsRender,
ChartChangedCallback));
public IScene Scene
{
get => (IScene)GetValue(SceneProperty);
set => SetValue(SceneProperty, value);
}
}
I want to bind this property to the viewModel and I was using following code in Xaml of ChartView.xaml to do so:
<local:ChartView
x:Name="ChartView"
Scene="{Binding Path=(viewModels:ChartViewModel.Scene)}"
>
But the problem is that this code is recurrently calling the constructor on the user control as I get stackOverflow exception in "InitializeComponent()" method. Even if I remove the scene binding from xaml then also exception is there. As soon as I add
<local:ChartView>
I start getting stack overflow error.
Can anyone point out the correct way to do it.
Thanks
You are getting a StackOverflowException because you are creating an instance of your UserControl class inside its XAML, like
<UserControl x:Class="YourNamespace:ChartView"
xmlns:local="clr-namespace:YourNamespace" ...>
<local:ChartView .../>
</UserControl>
You should obviously not do that. Instead, bind the UserControl's Scene property to a view model property when you use it, e.g. in your MainWindow:
<Window ...>
<Window.DataContext>
<local:ChartViewModel/>
</Window.DataContext>
<Grid>
<local:ChartView Scene="{Binding Scene}"/>
</Grid>
</Window>
You may also create a default Style for your UserControl (e.g. in App.xaml), that sets up the Binding:
<Application.Resources>
<Style TargetType="local:ChartView">
<Setter Property="Scene" Value="{Binding Scene}"/>
</Style>
</Application.Resources>
In WPF I have a window that includes a user control. The window and user control each have a view model. I want to pass a parameter from the window's VM to the UC's VM. After a fair amount of looking, I haven't found a way.
The window XAML sets its data context to its VM. The UC includes a custom dependency property for the parameter. I want to use SetBinding to bind the DP to the UC VM.
If I set the UC data context to its VM, then the parameter binding doesn't work. If I don't set the UC data context then the parameter binding works but the UC VM is not referenced.
How can I pass a parameter AND bind to the UC VM?
UC XAML
<UserControl x:Name="userControl" x:Class="Test_Paramaterized_UserControl_with_MVVM.UserControl1"
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:Test_Paramaterized_UserControl_with_MVVM"
xmlns:view="clr-namespace:Daavlin.SmartTouch.STUV_WPF.View"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300">
<Grid Margin="10">
<Border BorderThickness="3" BorderBrush="Black" Padding="10">
<StackPanel HorizontalAlignment="Center" VerticalAlignment="Center">
<StackPanel Orientation="Horizontal">
<TextBlock Text="UserControl1 View: "/>
<TextBlock Text="{Binding ElementName=userControl, Path=PropUserControlView, Mode=OneWay}" FontWeight="Bold"/>
</StackPanel>
<Rectangle Height="5"/>
<StackPanel Orientation="Horizontal">
<TextBlock Text="UserControl1 ViewModel: " />
<TextBlock Text="{Binding PropUserControlViewModel, FallbackValue=propUserControlViewModel 2}" FontWeight="Bold">
<TextBlock.DataContext>
<local:UserControl1ViewModel/>
</TextBlock.DataContext>
</TextBlock>
</StackPanel>
</StackPanel>
</Border>
</Grid>
</UserControl>
UC code-behind & VM
public partial class UserControl1 : UserControl
{
public UserControl1()
{
InitializeComponent();
}
public string PropUserControlView { get => (string)GetValue(PropUserControlViewProperty); set => SetValue(PropUserControlViewProperty, value); }
public static readonly DependencyProperty PropUserControlViewProperty =
DependencyProperty.Register(nameof(PropUserControlView), typeof(string), typeof(UserControl1),
new PropertyMetadata(null, DependencyPropertyChanged));
private static void DependencyPropertyChanged(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs dependencyPropertyChangedEventArgs)
{
var x = dependencyPropertyChangedEventArgs.NewValue;
}
}
public class UserControl1ViewModel : ObservableObject
{
public string PropUserControlViewModel { get => _propUserControlViewModel; set => SetField(ref _propUserControlViewModel, value); }
private string _propUserControlViewModel = "value from UserControl-ViewModel";
}
Window XAML
<Window x:Class="Test_Paramaterized_UserControl_with_MVVM.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:Test_Paramaterized_UserControl_with_MVVM"
Title="MainWindow" >
<Window.DataContext>
<local:MainWindowViewModel />
</Window.DataContext>
<Grid VerticalAlignment="Top" >
<StackPanel VerticalAlignment="Center" HorizontalAlignment="Center" Margin="20">
<StackPanel Orientation="Horizontal">
<TextBlock Text="MainWindow1 ViewModel: "/>
<TextBox Text="{Binding PropWindowViewModel, UpdateSourceTrigger=PropertyChanged}" FontWeight="Bold"/>
</StackPanel>
<Rectangle Height="10"/>
<StackPanel Orientation="Horizontal">
<TextBlock Text="UserControl1 (fixed value Fixed): " VerticalAlignment="Center"/>
<local:UserControl1 PropUserControlView="Fixed"/>
</StackPanel>
<Rectangle Height="10"/>
<StackPanel Orientation="Horizontal">
<TextBlock Text="UserControl1 (bound to MainWindows VM): " VerticalAlignment="Center"/>
<local:UserControl1 PropUserControlView="{Binding PropWindowViewModel}"/>
</StackPanel>
</StackPanel>
</Grid>
</Window>
Window code-behind & VM
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
}
public class MainWindowViewModel : ObservableObject
{
public string PropWindowViewModel { get => _propWindowViewModel; set => SetField(ref _propWindowViewModel, value); }
private string _propWindowViewModel = "valuefrom Window-VIewModel";
}
As far as I understood, what you meant was :-
1) You have a user control which has its own view model.
2) You have a Window where you have its own view model.
You want to link both and pass parameters from your WindowViewModel to UserControlViewModel.
What you can do is, Keep a property (e.g. UCViewModel) of type UserControlViewModel in your WindowViewModel and set the datacontext of the user control in your XAML to
<local:UserControl1 DataContext="{Binding UCViewModel}" .../>
Now that you can access anything that is there in your UserControlViewModel via WindowViewModel, you can set any property value OR pass any parameter to your UserControlViewModel from WindowViewModel.
If you need a code reference, let me know. We have been using user controls in a similar way and it works fine.
I want to use SetBinding to bind the DP to the UC VM.
Is that really a requirement? SetBinding() requires that the target property be a dependency property, which in turn requires that the target object be a dependency object. Your view model object is not a dependency object, and of course none of its properties are dependency properties.
Achieving that goal would require a much bigger change to your code than is otherwise apparently necessary.
If I set the UC data context to its VM, then the parameter binding doesn't work
Why not? You didn't show code that attempts this, so it's difficult to understand what you mean here. It's not a good idea to have the user control set its own DataContext anyway. That property is public, and you don't want to expose your implementation details to client code. Doing so invites bugs where the client code has set the DataContext to the wrong thing, disabling everything in your UserControl.
But that said, if by "parameter binding" you mean the binding in the MainWindow XAML, assigning {Binding PropWindowViewModel} to the PropUserControlView property of the user control, then just setting the DataContext of the user control should not affect that. You still have the dependency property in the user control, and anything bound that within the user control should still work.
Finally, it's not entirely clear why you want the dependency property tied to the view model. In the user control's XAML, you can (as you've already done) bind directly to the user control's dependency property. There's no need for a property in the view model to replicate that.
Maybe you have code in the view model somewhere else that wants to respond to changes in this value? It's not clear, and it's difficult to give the best advice without knowing the whole story.
All that said, the code you posted above can be made to work with a couple of small changes. First, you'll need to expose the TextBlock where you've created the view model, so that the user control code-behind has access to it:
<TextBlock x:Name="textBlock1" Text="{Binding PropUserControlViewModel, FallbackValue=propUserControlViewModel 2}" FontWeight="Bold">
<TextBlock.DataContext>
<l:UserControl1ViewModel/>
</TextBlock.DataContext>
</TextBlock>
I.e. add the x:Name="textBlock1" to the declaration.
Then, you need to use the property-change notification for your dependency property to update the view model property any time the dependency property changes:
private static void DependencyPropertyChanged(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs dependencyPropertyChangedEventArgs)
{
UserControl1 uc = (UserControl1)dependencyObject;
UserControl1ViewModel vm = (UserControl1ViewModel)uc.textBlock1.DataContext;
vm.PropUserControlViewModel = (string)dependencyPropertyChangedEventArgs.NewValue;
}
The above works in your limited example, but you'll probably want to give the DependencyPropertyChanged() method a more descriptive name, specific to the actual property in question.
If you do choose to mirror the dependency property in the view model this way, IMHO a better way to do that would be to set the user control's root element (i.e. the Grid) so that its data context is your view model, and then throughout the rest of the XAML, bind only to the view model. Mixing the view model and dependency property is not wrong per se, but it does introduce an inconsistency that can make it harder to test and maintain the code.
[Edit]: I figured out how to do this on my own. I posted my solution in the hope that it will save someone else a few days of Googling. If you are a WPF guru, please look at my solution and let me know if there is a better / more elegant / more efficient way to do this. In particular, I am interested in knowing what I don't know... how is this solution going to screw me down the road? The problem really boils down to exposing inner control properties.
Problem:
I am creating some code to auto-generate a data-bound GUI in WPF for an XML file. I have an xsd file that can help me determine the node types, etc. Simple Key/Value elements are easy.
When I parse this element:
<Key>value</Key>
I can create a new 'KeyValueControl' and set the DataContext to this element. The KeyValueControl is defined as a UserControl and just has some simple bindings on it. It works great for any simple XElement.
The XAML inside this control looks like this:
<Label Content={Binding Path=Name} />
<TextBox Text={Binding Path=Value} />
The result is a line that has a label with the element name and a text box with the value that I can edit.
Now, there are times where I need to display lookup values instead of the actual value. I would like to create a 'KeyValueComboBox' similar to the above KeyValueControl but be able to specify (based on information in the file) the ItemsSource, DisplayMemberPath, and ValueMemberPath. The 'DisplayMemberPath' and 'ValueMemberPath' bindings would be the same as the KeyValueControl.
I don't know if a standard user control can handle this, or if I need to inherit from Selector.
The XAML in the control would look something like this:
<Label Content={Binding Path=Name} />
<ComboBox SelectedValue={Binding Path=Value}
ItemsSource={Binding [BOUND TO THE ItemsSource PROPERTY OF THIS CUSTOM CONTROL]
DisplayMemberPath={Binding [BOUND TO THE DisplayMemberPath OF THIS CUSTOM CONTROL]
SelectedValuePath={Binding [BOUND TO THE SelectedValuePath OF THIS CUSTOM CONTROL]/>
In my code, I would then do something like this (assuming that this node is a 'Thing' and needs to display a list of Things so the user can select the ID:
var myBoundComboBox = new KeyValueComboBox();
myBoundComboBox.ItemsSource = getThingsList();
myBoundComboBox.DisplayMemberPath = "ThingName";
myBoundComboBox.ValueMemberPath = "ThingID"
myBoundComboBox.DataContext = thisXElement;
...
myStackPanel.Children.Add(myBoundComboBox)
So my questions are:
1) Should I inherit my KeyValueComboBox from Control or Selector?
2) If I should inherit from Control, how do I expose the inner Combo Box's ItemsSource, DisplayMemberPath, and ValueMemberPath for binding?
3) If I need to inherit from Selector, can someone provide a small example of how I might get started with that? Again, I'm new to WPF so a nice, simple example would really help if that's the road I need to take.
I ended up figuring how how to do this on my own. I'm posting the answer here so that others can see a solution that works, and maybe a WPF guru will come by and show me a better/more elegant way to do this.
So, the answer ended up being #2. Exposing the inner properties turns out to be the right answer. Setting it up is actually pretty easy.. once you know how to do it. There aren't many complete examples of this (that I could find), so hopefully this one will help someone else that runs into this problem.
ComboBoxWithLabel.xaml.cs
The important thing in this file is the use of DependencyProperties. Note that all we're doing right now is just exposing the properties (LabelContent and ItemsSource). The XAML will take care of wiring the internal control's properties to these external properties.
namespace BoundComboBoxExample
{
/// <summary>
/// Interaction logic for ComboBoxWithLabel.xaml
/// </summary>
public partial class ComboBoxWithLabel : UserControl
{
// Declare ItemsSource and Register as an Owner of ComboBox.ItemsSource
// the ComboBoxWithLabel.xaml will bind the ComboBox.ItemsSource to this
// property
public IEnumerable ItemsSource
{
get { return (IEnumerable)GetValue(ItemsSourceProperty); }
set { SetValue(ItemsSourceProperty, value); }
}
public static readonly DependencyProperty ItemsSourceProperty =
ComboBox.ItemsSourceProperty.AddOwner(typeof(ComboBoxWithLabel));
// Declare a new LabelContent property that can be bound as well
// The ComboBoxWithLable.xaml will bind the Label's content to this
public string LabelContent
{
get { return (string)GetValue(LabelContentProperty); }
set { SetValue(LabelContentProperty, value); }
}
public static readonly DependencyProperty LabelContentProperty =
DependencyProperty.Register("LabelContent", typeof(string), typeof(ComboBoxWithLabel));
public ComboBoxWithLabel()
{
InitializeComponent();
}
}
}
ComboBoxWithLabel.xaml
The XAML is pretty straightforward, with the exception of the bindings on the Label and the ComboBox ItemsSource. I found that the easiest way to get these bindings right is to declare the properties in the .cs file (as above) and then use the VS2010 designer to setup the binding source from the properties pane. Essentially, this is the only way I know of to bind an inner control's properties to the base control. If there's a better way to do it, please let me know.
<UserControl x:Class="BoundComboBoxExample.ComboBoxWithLabel"
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"
mc:Ignorable="d"
d:DesignHeight="28" d:DesignWidth="453" xmlns:my="clr-namespace:BoundComboBoxExample">
<Grid>
<DockPanel LastChildFill="True">
<!-- This will bind the Content property on the label to the 'LabelContent'
property on this control-->
<Label Content="{Binding Path=LabelContent,
RelativeSource={RelativeSource FindAncestor,
AncestorType=my:ComboBoxWithLabel,
AncestorLevel=1}}"
Width="100"
HorizontalAlignment="Left"/>
<!-- This will bind the ItemsSource of the ComboBox to this
control's ItemsSource property -->
<ComboBox ItemsSource="{Binding RelativeSource={RelativeSource FindAncestor,
AncestorType=my:ComboBoxWithLabel,
AncestorLevel=1},
Path=ItemsSource}"></ComboBox>
<!-- you can do the same thing with SelectedValuePath,
DisplayMemberPath, etc, but this illustrates the technique -->
</DockPanel>
</Grid>
</UserControl>
MainWindow.xaml
The XAML to use this is not interesting at all.. which is exactly what I wanted. You can set the ItemsSource and the LabelContent via all the standard WPF techniques.
<Window x:Class="BoundComboBoxExample.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="86" Width="464" xmlns:my="clr-namespace:BoundComboBoxExample"
Loaded="Window_Loaded">
<Window.Resources>
<ObjectDataProvider x:Key="LookupValues" />
</Window.Resources>
<Grid>
<my:ComboBoxWithLabel LabelContent="Foo"
ItemsSource="{Binding Source={StaticResource LookupValues}}"
HorizontalAlignment="Left"
Margin="12,12,0,0"
x:Name="comboBoxWithLabel1"
VerticalAlignment="Top"
Height="23"
Width="418" />
</Grid>
</Window>
For Completeness Sake, here is the MainWindow.xaml.cs
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void Window_Loaded(object sender, RoutedEventArgs e)
{
((ObjectDataProvider)FindResource("LookupValues")).ObjectInstance =
(from i in Enumerable.Range(0, 5)
select string.Format("Bar {0}", i)).ToArray();
}
}
I tried your solution but it fails for me. It does not pass the value over to inner control at all. What I did is declaration of same dependency properties in outer control and bound inner to outer like that:
// Declare IsReadOnly property and Register as an Owner of TimePicker (base InputBase).IsReadOnly the TimePickerEx.xaml will bind the TimePicker.IsReadOnly to this property
// does not work: public static readonly DependencyProperty IsReadOnlyProperty = InputBase.IsReadOnlyProperty.AddOwner(typeof(TimePickerEx));
public static readonly DependencyProperty IsReadOnlyProperty = DependencyProperty.Register("IsReadOnly", typeof (bool), typeof (TimePickerEx), new PropertyMetadata(default(bool)));
public bool IsReadOnly
{
get { return (bool) GetValue(IsReadOnlyProperty); }
set { SetValue(IsReadOnlyProperty, value); }
}
Than in xaml:
<UserControl x:Class="CBRControls.TimePickerEx" x:Name="TimePickerExControl"
...
>
<xctk:TimePicker x:Name="Picker"
IsReadOnly="{Binding ElementName=TimePickerExControl, Path=IsReadOnly}"
...
/>
</UserControl>
I have some issues with my DependencyProperty in a custom UserControl.
I need to display informations about people in a particular way. To achieve this, I have several UserControls that receive a List<PeopleList> which contains (obviously) one or more People.
Let me show you my (simplified) code and I'll then explain to you the actual behavior of my app.
Here is my UserControl :
public abstract class PeopleLine : UserControl
{
public static readonly DependencyProperty PeopleListProperty =
DependencyProperty.Register("PeopleList", typeof(List<PeopleModel>), typeof(PeopleLine), new PropertyMetadata(default(List<PeopleModel>)));
public List<PeopleModel> PeopleList
{
get { return (List<PeopleModel>)GetValue(PeopleListProperty); }
set { SetValue(PeopleListProperty, value); }
}
}
Then my xaml :
<local:PeopleLine
x:Class="MyApp.Controls.EventSheet.OnePeople"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:MyApp.Controls.EventSheet"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d">
<Grid
Margin="0 5"
VerticalAlignment="Top"
Height="51">
<TextBlock
Grid.Column="1"
HorizontalAlignment="Center"
Foreground="Red"
FontSize="25"
Text="{Binding PeopleList[0].Name}"/>
</Grid>
</local:PeopleLine>
And this all starts with my Page which contains an ItemsControl with a correct ItemsSource (I already checked it) and an ItemTemplateSelector (also working perfectly). Here is one of the DataTemplate used by the selector :
<DataTemplate x:Key="OnePeople">
<peoplecontrols:OnePeople
PeopleList="{Binding LinePeopleList}"/>
</DataTemplate>
I'm using several Models That are not really important here since I simplified my code to only have the most important information.
So, back to my issue. When replacing the peoplecontrols:OnePeople in the selector's DataTemplate by a string and putting LinePeopleList[0].Nameas Text, I have the correct text displayed, proving me that my data is correct at this point.
Problem is that when putting back my peoplecontrols:OnePeople, my DependencyProperty is never set. I put a breakpoint at PeopleList's setter and it never triggers.
I tried several modifications (especially those that are given in this post, so replacing the typeof(List<PeopleModel>)by typeof(object) has already been tried) with no success. Also, I tried to replace my DependencyProperty to a string and directly send the name in the DataTemplate but the setter is still not called...
I have no more ideas now and don't understand what's wrong with my code. Any help would be greatly appreciated.
Thanks in advance.
Thomas
Try adding the following line in your UserControl's Constructor, after the call to InitializeComponent:
(this.Content as FrameworkElement).DataContext = this;
I created a sample app on regarding this. Hopefully it reflects your situation correctly:
https://github.com/mikoskinen/uwpusercontrollistdp
If you clone the app and run it, you'll notice that the binding doesn't work. But if you uncomment the Datacontext = this line from UserControl, everything should work OK. Here's working code:
public PeopleLine()
{
this.InitializeComponent();
(this.Content as FrameworkElement).DataContext = this;
}