How to access the content of my usercontrol - c#

I created a user-control which is a button. When implementing the usercontrol into a parrent control, I'd like to set the icon and tooltip aswell as an enum property that indicates the buttons behaviour.
This is my Button.xaml
<UserControl x:Class="Messenger.Controls.MainWindow.Button"
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="30" d:DesignWidth="30">
<Grid>
<Button Name="btTabButton" Height="30" Width="30">
<Image x:Name="Icon" HorizontalAlignment="Center" VerticalAlignment="Center"/>
<Button.ToolTip>
<ToolTip x:Name="Hover">
<TextBlock x:Name="HoverText"/>
</ToolTip>
</Button.ToolTip>
</Button>
</Grid>
And the Button.xaml.cs
namespace Messenger.Controls.MainWindow
{
public partial class Button : UserControl
{
public Button()
{
InitializeComponent();
}
Image img;
public Image Img
{
get { return img; }
set { img = value; this.Icon = img; }
}
string hTxt;
public string HTxt
{
get { return hTxt; }
set { hTxt = value; this.HoverText.Text = hTxt; }
}
}
}
(The function-selection is not implemented yet)
Here I call the usercontrol and try to set the property
xmlns:bt="clr-namespace:Messenger.Controls.MainWindow"
<StackPanel Name="spButtons" Height="30" VerticalAlignment="Top" Orientation="Horizontal">
<bt:Button Name="one" >
<bt:Button.HText >
Test
</bt:Button.HText>
</bt:Button>
<bt:Button Name="two" />
<bt:Button Name="tre" />
<bt:Button Name="for" />
</StackPanel>
But I get an error-message:
"The member "HText" is unknown or the member can't be accessed"
//"The attached "HText"-property can't be found in 'Button'" This message does not appear anymore

Related

INotifyPropertyChanged Event handler is always null

I use .NETFramework,Version=v4.6.1
I have a Window, MainWindow. This is the XAML:
<Window x:Class="VexLibrary.DesktopClient.Views.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:VexLibrary.DesktopClient.Views"
Title="MainWindow" Height="600" Width="800">
<Grid>
<StackPanel>
<Grid Style="{StaticResource TitleBar}">
<Border Style="{StaticResource TitleBarBorder}">
<DockPanel>
<StackPanel DockPanel.Dock="Left" Orientation="Horizontal">
<TextBlock Style="{StaticResource TitleBarIcon}" Text="" />
<Label Style="{StaticResource TitleBarTitle}" Content="{Binding Path=CurrentPageTitle, UpdateSourceTrigger=PropertyChanged}" ></Label>
</StackPanel>
<StackPanel DockPanel.Dock="Right" Orientation="Horizontal" HorizontalAlignment="Right">
<Label Style="{StaticResource TitleBarTime}">12:05 AM</Label>
<StackPanel Orientation="Horizontal">
<Label Style="{StaticResource TitleBarUsername}">Hassan</Label>
<Button>
<TextBlock Style="{StaticResource TitleBarIcon}" Text="" />
</Button>
</StackPanel>
</StackPanel>
</DockPanel>
</Border>
</Grid>
<Frame Width="700" Height="507" Source="Pages/Dashboard.xaml" />
</StackPanel>
</Grid>
</Window>
Note the:
<Label Style="{StaticResource TitleBarTitle}" Content="{Binding Path=CurrentPageTitle, UpdateSourceTrigger=PropertyChanged}" ></Label>
The DataContext is set as follows in the MainWindow.xaml.cs constructor:
this.DataContext = new MainViewModel();
In the <Frame>, a Page Dashboard.xamlis loaded.
The page Dashboard.xaml has the source:
<Page x:Class="VexLibrary.DesktopClient.Views.Pages.Dashboard"
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:VexLibrary.DesktopClient.Views.Pages"
mc:Ignorable="d"
d:DesignHeight="460" d:DesignWidth="690"
Title="Page1">
<Grid Width="690" Height="460" HorizontalAlignment="Center" VerticalAlignment="Center">
<!-- Members, Users, Books -->
<!-- Returns, Subscriptions, Statistics -->
<Grid.ColumnDefinitions>
<ColumnDefinition Width="1*" />
<ColumnDefinition Width="1*" />
<ColumnDefinition Width="1*" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="1*" />
<RowDefinition Height="1*" />
</Grid.RowDefinitions>
<Button Style="{StaticResource MenuButton}" Grid.Column="0" Grid.Row="0"></Button>
<Button Style="{StaticResource MenuButton}" Grid.Column="0" Grid.Row="1"></Button>
<Button Style="{StaticResource MenuButton}" Grid.Column="1" Grid.Row="0"></Button>
<Button Style="{StaticResource MenuButton}" Grid.Column="1" Grid.Row="1"></Button>
<Button Style="{StaticResource MenuButton}" Grid.Column="2" Grid.Row="0"></Button>
<Button Style="{StaticResource MenuButton}" Grid.Column="2" Grid.Row="1" Command="{Binding ViewStatistics}"></Button>
</Grid>
</Page>
In the Dashboard.xaml.cs constructor, I have defined the DataContext like this: DataContext = new DashboardViewModel();
The DashboardViewModel.cs source code is like this (omitted namespaces)
namespace VexLibrary.DesktopClient.ViewModels
{
class DashboardViewModel : ViewModel
{
private MainViewModel parentViewModel;
public DashboardViewModel()
{
this.parentViewModel = new MainViewModel();
}
public ICommand ViewStatistics
{
get
{
return new ActionCommand(p => this.parentViewModel.LoadPage("Statistics"));
}
}
}
}
Now, in this code, notice the Button with the Command:
<Button Style="{StaticResource MenuButton}" Grid.Column="2" Grid.Row="1" Command="{Binding ViewStatistics}"></Button>
It successfully calls the Command and the parent LoadPage method is executed correctly. The parent viewmodel looks like this:
namespace VexLibrary.DesktopClient.ViewModels
{
public class MainViewModel : ViewModel
{
private string currentPageTitle;
public string CurrentPageTitle
{
get
{
return this.currentPageTitle;
}
set
{
currentPageTitle = value;
NotifyPropertyChanged();
}
}
public void LoadPage(string pageName)
{
this.CurrentPageTitle = pageName;
Console.WriteLine(CurrentPageTitle);
}
}
}
The CurrentPageTitle is successfully updated. However, it is not updated in the view.
The parent view model inherits ViewModel which basically has this code:
namespace VexLibrary.Windows
{
public abstract class ViewModel : ObservableObject, IDataErrorInfo
{
public string this[string columnName]
{
get
{
return OnValidate(columnName);
}
}
[Obsolete]
public string Error
{
get
{
throw new NotImplementedException();
}
}
protected virtual string OnValidate(string propertyName)
{
var context = new ValidationContext(this)
{
MemberName = propertyName
};
var results = new Collection<ValidationResult>();
bool isValid = Validator.TryValidateObject(this, context, results, true);
if (!isValid)
{
ValidationResult result = results.SingleOrDefault(p =>
p.MemberNames.Any(memberName =>
memberName == propertyName));
return result == null ? null : result.ErrorMessage;
}
return null;
}
}
}
ObservableObject.cs:
namespace VexLibrary.Windows
{
public class ObservableObject : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
// [CallerMemberName] automatically resolves the property name for us.
protected void NotifyPropertyChanged([CallerMemberName] string propertyName = "")
{
PropertyChangedEventHandler handler = PropertyChanged;
Console.WriteLine(handler == null);
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
}
}
After debugging, I found out, the NotifyPropertyChanged is invoked, but the handler is always null. How do I fix this? This is not updating the text in the MainWindow.xaml. I tested to see if the property value is changed, and yes, it is changed in the MainViewModel.cs
Also, I tested whether the label itself is visible or not. For that, I gave the variable a value and it correctly displays, but it is not updated.
The DashboardViewModel is instantiating a new instance of the MainViewModel rather than using the instance assigned to the DataContext of the MainWindow (and therefore the instance the view is bound to).
For your code to work you need to pass the correct instance of the MainViewModel to the DashboardViewModel as it is this instance that will have a handler for the property changed event.
EDIT: As per the comment below, you should instantiate your sub viewmodels as follows:
namespace VexLibrary.DesktopClient.ViewModels
{
public class MainViewModel : ViewModel
{
private ViewModel _currentViewModel;
public MainViewModel()
{
_currentViewModel = new DashboardViewModel(this);
}
public ViewModel CurrentViewModel
{
get { return _currentViewModel; }
private set
{
_currentViewModel = value;
OnPropertyChanged();
}
}
}
}
You can then amend your Xaml such that the frame gets it's data context from the CurrentViewModel property as follows:
<Window x:Class="VexLibrary.DesktopClient.Views.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:VexLibrary.DesktopClient.Views"
Title="MainWindow" Height="600" Width="800">
<Grid>
<StackPanel>
<Grid Style="{StaticResource TitleBar}">
<Border Style="{StaticResource TitleBarBorder}">
<DockPanel>
<StackPanel DockPanel.Dock="Left" Orientation="Horizontal">
<TextBlock Style="{StaticResource TitleBarIcon}" Text="" />
<Label Style="{StaticResource TitleBarTitle}" Content="{Binding Path=CurrentPageTitle, UpdateSourceTrigger=PropertyChanged}" ></Label>
</StackPanel>
<StackPanel DockPanel.Dock="Right" Orientation="Horizontal" HorizontalAlignment="Right">
<Label Style="{StaticResource TitleBarTime}">12:05 AM</Label>
<StackPanel Orientation="Horizontal">
<Label Style="{StaticResource TitleBarUsername}">Hassan</Label>
<Button>
<TextBlock Style="{StaticResource TitleBarIcon}" Text="" />
</Button>
</StackPanel>
</StackPanel>
</DockPanel>
</Border>
</Grid>
<Frame Width="700" Height="507" Source="Pages/Dashboard.xaml" DataContext="{Binding CurrentViewModel}"/>
</StackPanel>
</Grid>
</Window>
And will then need to use some form of view location / navigation to change the frame to display the correct view. Some MVVM frameworks (for example, CaliburnMicro) can do this for you.
Again, in order to make this code testable, the instantiation of sub-viewmodels should be delegated to a factory class which is injected into the MainViewModel.
Hope it helps.

How to dynamically change DataTemplate according to bound object's type?

I'm trying to create a DataTemplate for a View, to show a specific UserControl type (like a texbox, combobox, custom control or another View) based on the type of object it is bound to.
I have the following MVVM framework:
FieldView is tied to an instance of FieldPresenter, and should display a <Textblock /> for the "Label" property, and a UserControl or another View for the Value (based on the Type of the value), with it's DataSource set to the Value property of the Presenter. Currently, I do not have the second part working. I can't figure out how to write a WPF template for what I need.
ViewModel:
public class FieldPresenter : Observable<object>, IFieldPresenter, INotifyPropertyChanged
{
public FieldPresenter() { }
public FieldPresenter(object value)
{
Value = value;
}
object IFieldPresenter.Value
{
get
{
return base.Value;
}
set
{
base.Value = value;
OnPropertyChanged("Value");
}
}
private string _label;
public virtual string Label
{
get
{
return _label;
}
private set
{
_label = value;
OnPropertyChanged("Label");
}
}
}
View:
<UserControl x:Class="My.Views.FieldView"
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:ViewModels="clr-namespace:My.ViewModels"
mc:Ignorable="d"
d:DesignHeight="24" d:DesignWidth="100">
<UserControl.DataContext>
<ViewModels:FieldPresenter/>
</UserControl.DataContext>
<UserControl.Template>
<ControlTemplate>
<Grid Margin="4">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" SharedSizeGroup="Key" />
</Grid.ColumnDefinitions>
<StackPanel Margin="0,0,0,0" HorizontalAlignment="Stretch" Width="{Binding RelativeSource={RelativeSource AncestorType=Grid}, Path=ActualWidth}">
<TextBlock Text="{Binding Label}" FontWeight="Bold" Height="32" HorizontalAlignment="Stretch"/>
<TextBox Text="{Binding Value}" Height="Auto" HorizontalAlignment="Stretch"/>
</StackPanel>
</Grid>
</ControlTemplate>
</UserControl.Template>
</UserControl>
I'm curious if what I'm trying to do is even possible, or if I can workaround it by making my Presenter viewmodel return a UserControl rather than an object value, and have the Presenter parse the UserControl Type from the object type, but I don't feel like my Presenter should be instantiating Controls (or what is technically an unbound view). Should I make an interface, something like IViewAs<controlType> { controlType View { get; } }?
How else would I replace <TextBox Text="{Binding Value}" /> in the above script with some kind of template of a UserControl based on the databound object's type?
You almost certainly want a ContentTemplateSelector :
Code:
using System.Windows;
using System.Windows.Controls;
namespace WpfApplication1
{
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
Primitive primitive;
primitive = new Sphere();
// primitive = new Cube();
DataContext = primitive;
}
}
internal abstract class Primitive
{
public abstract string Description { get; }
}
internal class Cube : Primitive
{
public override string Description
{
get { return "Cube"; }
}
}
internal class Sphere : Primitive
{
public override string Description
{
get { return "Sphere"; }
}
}
public class MyTemplateSelector : DataTemplateSelector
{
public override DataTemplate SelectTemplate(object item, DependencyObject container)
{
var frameworkElement = container as FrameworkElement;
if (frameworkElement != null && item != null)
{
if (item is Cube)
{
return frameworkElement.FindResource("CubeTemplate") as DataTemplate;
}
if (item is Sphere)
{
return frameworkElement.FindResource("SphereTemplate") as DataTemplate;
}
}
return base.SelectTemplate(item, container);
}
}
}
XAML:
<Window x:Class="WpfApplication1.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:local="clr-namespace:WpfApplication1"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
x:Name="Window"
Title="MainWindow"
Width="525"
Height="350"
mc:Ignorable="d">
<Grid>
<Grid.Resources>
<local:MyTemplateSelector x:Key="myTemplateSelector" />
<DataTemplate x:Key="CubeTemplate" DataType="local:Cube">
<Border BorderBrush="Blue"
BorderThickness="1"
CornerRadius="5" />
</DataTemplate>
<DataTemplate x:Key="SphereTemplate" DataType="local:Sphere">
<Border BorderBrush="Red"
BorderThickness="1"
CornerRadius="50" />
</DataTemplate>
</Grid.Resources>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="1*" />
<RowDefinition />
</Grid.RowDefinitions>
<Label Grid.Row="0"
Content="{Binding Description}"
d:DataContext="{d:DesignInstance local:Primitive}" />
<ContentControl Grid.Row="1"
Content="{Binding}"
ContentTemplateSelector="{StaticResource myTemplateSelector}" />
</Grid>
</Window>
Result:
See the documentation for more:
https://msdn.microsoft.com/en-us/library/system.windows.controls.datatemplateselector(v=vs.110).aspx

Defining UserControl Properties and binding them in windows phone

In my windows phone application, I made a user control that has some text blocks and images. It's something like a control to show posts like Facebook. I want to use this user control in a long list selector, but whenever I try to bind data to it I get no data. Please help me.
This is the MainPage.xaml
<Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">
<phone:LongListSelector Name="myLLS">
<phone:LongListSelector.ItemTemplate>
<DataTemplate>
<local:Posts TitleText={Binding TitleText}>
</local:Posts>
</DataTemplate>
</phone:LongListSelector.ItemTemplate>
</phone:LongListSelector>
</Grid>
And this is the code behind
public class TestData
{
private string _TitleText;
public string TitleText
{
get { return _TitleText; }
set { _TitleText = value; }
}
public TestData(string Text)
{
This.TitleText = Text;
}
}
And this is the UserControl xaml code
<UserControl x:Class="BindingUserControlTest.TestBind"
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"
mc:Ignorable="d"
FontFamily="{StaticResource PhoneFontFamilyNormal}"
FontSize="{StaticResource PhoneFontSizeNormal}"
Foreground="{StaticResource PhoneForegroundBrush}" Height="125.889" Width="227.974">
<Grid x:Name="LayoutRoot" Background="{StaticResource PhoneChromeBrush}">
<TextBlock x:Name="lblTitle" HorizontalAlignment="Left" TextWrapping="Wrap" Text="TextBlock" VerticalAlignment="Top"/>
<TextBlock x:Name="lblDescription" HorizontalAlignment="Left" TextWrapping="Wrap" Text="TextBlock" VerticalAlignment="Top" Margin="0,32,0,0" Width="218" Height="84"/>
</Grid>
And this is the code behind :
private string _ShownTitle;
public string ShownTitle { get { return _ShownTitle; } set { _ShownTitle = value; }}
You need to use DependencyProperties to be able to bind to an attribute. Rather than re-write the code for you, I will give you a link to a blog by Jerry Nixon, who explains the process well.
http://blog.jerrynixon.com/2013/07/solved-two-way-binding-inside-user.html
EDIT:
Code Behind for the Usercontrol would look like.
public sealed partial class ExampleControl : UserControl
{
public static readonly DependencyProperty exampleProperty = DependencyProperty.Register("ExampleData", typeof(Double), typeof(NutritionLabelControl), null);
public ExampleControl()
{
InitializeComponent();
(this.Content as FrameworkElement).DataContext = this;
}
public Double ExampleData
{
get { return (Double)GetValue(exampleProperty); }
set
{
SetValue(exampleProperty, value);
}
}
}
Then in your userControls XAML you would have something like:
<UserControl>
<Grid x:Name="LayoutRoot">
<TextBlock Text="{Binding ExampleData}" />
</Grid>
</UserControl>
You can then use the same Binding format in the MainPage.xaml as in the usercontrols XAML.

ErrorTemplate showing for the user control and not the text box within it! How to fix?

There are similar questions to this one on here however I have tried the mentioned solutions to no avail!
Let me just run you through the setup - I have a model which implements IDataErrorInfo, a viewmodel which exposes the model to the view, within the view I have a usercontrol which is simply a labelled textbox, the model properties are binded to the usercontrol's inner textbox via a dependency property... and everything is binding correctly, all validation is fired and the correct errors returned! However, the usercontrol appears to be intercepting the error and thus the errortemplate of the usercontrol is displayed and not the textbox.
So, I know I can stop the usercontrol's error template from being displayed by setting the property to x:Null, however how do I trigger the textbox's error template to be displayed?! I have tried implementing IDataErrorInfo within the usercontrol (as advised by some) and explicitly defining the validation error template within the user control but I just can't get the darn thing to display. At this point I am thinking that the usercontrol is simply intercepting the error, holding onto it and not passing it onto the textbox, hence the errortemplate not being shown as it isn't aware of the error.
I have been pulling my hair out for the past day and really don't want to resort to not using the usercontrol as I know this can be achieved but I really don't know how to fix it! So if there are any wizards out there that can help I would be very grateful!
UserControl XAML:
<UserControl x:Class="PIRS_Client.Control.LabelTextBox"
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" Height="40.541" Width="321.027">
<Grid Height="41" VerticalAlignment="Top" HorizontalAlignment="Left" Width="321">
<StackPanel Orientation="Horizontal" Margin="0,8,50,9">
<Label Content="Label" Height="28" Name="BaseLabel" VerticalAlignment="Top" HorizontalContentAlignment="Right" Width="116" FontSize="11" />
<TextBox Height="22" Width="100" Margin="0,0,0,0" x:Name="BaseTextBox" VerticalContentAlignment="Center" VerticalAlignment="Top" FontSize="11"/>
</StackPanel>
</Grid>
UserControl Code:
public partial class LabelTextBox : UserControl
{
public static readonly DependencyProperty TextBoxTextProperty = DependencyProperty.Register("TextBoxText", typeof(string), typeof(LabelTextBox), new FrameworkPropertyMetadata() { BindsTwoWayByDefault = true });
public LabelTextBox()
{
InitializeComponent();
Binding textBoxText = new Binding("TextBoxText") { Source = this, Mode = BindingMode.TwoWay };
BaseTextBox.SetBinding(TextBox.TextProperty, textBoxText);
}
[Browsable(true)]
public string LabelText
{
get { return BaseLabel.Content.ToString(); }
set
{
BaseLabel.Content = value;
}
}
[Browsable(true)]
public string TextBoxText
{
get { return (string)GetValue(TextBoxTextProperty); }
set { SetValue(TextBoxTextProperty, value); }
}
[Browsable(true)]
public double TextBoxWidth
{
get { return BaseTextBox.Width; }
set
{
BaseTextBox.Width = value;
}
}
}
View - UserControl delcaration:
<control:LabelTextBox HorizontalAlignment="Left" LabelText="Email" TextBoxText="{Binding UpdateSourceTrigger=LostFocus, Path=NewFosterCarerInfo.partner_email, ValidatesOnDataErrors=true, NotifyOnValidationError=true}" TextBoxWidth="120" Margin="190,182,-61,0" VerticalAlignment="Top" Height="41" Width="321"/>
For anyone with my problem, here is the working code
UserControl xaml:
<UserControl x:Class="PIRS_Client.Control.LabelTextBox"
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" Height="40.541" Width="321.027"
x:Name="Parent" Validation.ErrorTemplate="{x:Null}">
<Grid Height="41" VerticalAlignment="Top" HorizontalAlignment="Left" Width="321" DataContext="{Binding ElementName=Parent, ValidatesOnDataErrors=True}">
<StackPanel Orientation="Horizontal" Margin="0,8,50,9">
<Label Content="Label" Height="28" Name="BaseLabel" VerticalAlignment="Top" HorizontalContentAlignment="Right" Width="116" FontSize="11" />
<TextBox Height="22" Text="{Binding Path=TextBoxText, ValidatesOnDataErrors=True}" Width="100" Margin="0,0,0,0" x:Name="BaseTextBox" VerticalContentAlignment="Center" VerticalAlignment="Top" FontSize="11"/>
</StackPanel>
</Grid>
UserControl code behind:
public partial class LabelTextBox : UserControl, IDataErrorInfo
{
public LabelTextBox()
{
InitializeComponent();
}
public static readonly DependencyProperty TextBoxTextProperty =
DependencyProperty.Register(
"TextBoxText",
typeof(string),
typeof(LabelTextBox),
new FrameworkPropertyMetadata(string.Empty, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault)
);
#region IDataErrorInfo Members
public string Error
{
get
{
if (Validation.GetHasError(this))
return string.Join(Environment.NewLine, Validation.GetErrors(this).Select(e => e.ErrorContent));
return null;
}
}
public string this[string columnName]
{
get
{
// use a specific validation or ask for UserControl Validation Error
if (Validation.GetHasError(this))
{
var error = Validation.GetErrors(this).FirstOrDefault(e => ((BindingExpression)e.BindingInError).TargetProperty.Name == columnName);
if (error != null)
return error.ErrorContent as string;
}
return null;
}
}
#endregion
[Browsable(true)]
public string LabelText
{
get { return BaseLabel.Content.ToString(); }
set { BaseLabel.Content = value; }
}
[Browsable(true)]
public string TextBoxText
{
get { return (string)GetValue(TextBoxTextProperty); }
set {
SetValue(TextBoxTextProperty, value);
}
}
[Browsable(true)]
public double TextBoxWidth
{
get { return BaseTextBox.Width; }
set { BaseTextBox.Width = value; }
}
}
Using the UserControl:
<control:LabelTextBox HorizontalAlignment="Left" LabelText="Email" TextBoxText="{Binding Path=NewFosterCarerInfo.partner_email, ValidatesOnDataErrors=true}" TextBoxWidth="120" Margin="190,182,-61,0" VerticalAlignment="Top" Height="41" Width="321"/>
And in case you wanted a nice Validation.ErrorTemplate:
`<Style TargetType="{x:Type TextBox}">
<Setter Property="VerticalAlignment" Value="Center" />
<Setter Property="Margin" Value="0,2,40,2" />
<Setter Property="Validation.ErrorTemplate">
<Setter.Value>
<ControlTemplate>
<DockPanel LastChildFill="true">
<Border Background="Red" DockPanel.Dock="right" Margin="5,0,0,0" Width="20" Height="20" CornerRadius="10"
ToolTip="{Binding ElementName=customAdorner, Path=AdornedElement.(Validation.Errors)[0].ErrorContent}">
<TextBlock Text="!" VerticalAlignment="center" HorizontalAlignment="center" FontWeight="Bold" Foreground="white">
</TextBlock>
</Border>
<AdornedElementPlaceholder Name="customAdorner" VerticalAlignment="Center" >
<Border BorderBrush="red" BorderThickness="1" />
</AdornedElementPlaceholder>
</DockPanel>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>`

UserControl with Dependency Property inside an ItemsControl: how to Bind element of ItemSource to x:Name of UserControl?

My class IFrame can contain other IFrames:
public interface IFrame
{
int Id { get; }
string Summary { get; }
BindableCollection<IFrame> SubFrames { get; set; }
}
To present an IFrame, I have a custom UserControl:
<UserControl x:Class="Views.FrameView"
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:Views="clr-namespace:Views"
mc:Ignorable="d"
x:Name="FrameXName">
<StackPanel Orientation="Horizontal">
<Label Content="{Binding ElementName=FrameXName, Path=Id}" Width="50"/>
</StackPanel>
</UserControl>
With codebehind:
public partial class FrameView : UserControl
{
public FrameView()
{
InitializeComponent();
}
public static DependencyProperty IdProperty = DependencyProperty.Register(
"Id", typeof(int), typeof(FrameView));
public int Id
{
get { return (int) GetValue(IdProperty); }
set { SetValue(IdProperty, value); }
}
}
And thus I can display a Frame in a FooView.xaml using:
<Views:FrameView x:Name="Frame1" Width="50" Height="50"/> IF I define the following in FooViewModel.cs:
public IFrame Frame1 { get { return Frames.ElementAt(1); } }
My Goal:
I want to have a FrameView displayed for every IFrame in a collection, for example:
<ItemsControl ItemsSource="{Binding Frames}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<Views:FrameView x:Name="{Binding}" Width="50" Height="50"/>
</StackPanel>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
Where public BindableCollection<IFrame> Frames is defined.
Unfortunately, this does not work, the compiler says MarkupExtensions are not allowed for Uid or Name property values, so '{Binding}' is not valid.
How can I achieve my goal? Many thanks in advance.
In your datatemplate you can replace
<Views:FrameView x:Name="{Binding}" Width="50" Height="50" />
with
<Views:FrameView Id="{Binding Path=Id}" Width="50" Height="50" />
should work fine

Categories

Resources