I have read lot of answer about this type of error but nothings to fix my problem.
my program runs with this error, but i would know why and how i could fix? or its known bug? Thnaks for your help
error for Appearance.Offset.X:
System.Windows.Data Error: 2 : Cannot find governing FrameworkElement or FrameworkContentElement for target element.
BindingExpression:Path=Appearance.Offset.X; DataItem=null; target element is 'TranslateTransform' (HashCode=62775401); target property is 'X' (type 'Double')
Same error for Appearance.Offset.Y
my xaml file
<UserControl
:
:
mc:Ignorable="d" d:DesignHeight="150" d:DesignWidth="70"
x:Name="usercontrol" ToolTip="{Binding ToolTip}"
VerticalAlignment = "Top" HorizontalAlignment = "Left" ClipToBounds="True"
cal:Message.Attach="[Event MouseEnter] = [Action MouseEnterInUC($eventArgs)];
[Event MouseLeftButtonDown] = [Action MouseLeftButtonDownOnUC($source, $mousepoint, $eventArgs)];
[Event MouseLeftButtonUp] = [Action MouseLeftButtonUp()]">
<Grid x:Name="Switch" Width="{Binding Path=Layout.Width}" Height="{Binding Path=Layout.Height}" >
<Image x:Name="Pushed" Source="{Binding Appearance.PushedImage, Mode=TwoWay}"
HorizontalAlignment="Center" VerticalAlignment="Center" >
:
:
</Image>
<Image x:Name="Normal" Source="{Binding Appearance.Image}"
HorizontalAlignment="Center" VerticalAlignment="Center" >
:
:
</Image>
<TextBlock Text="{Binding Appearance.GlyphText}" Foreground="{Binding Appearance.TextColor, Converter={StaticResource MyconverterColorToSolidColorBrush}}"
HorizontalAlignment="{Binding Appearance.SelectedHAlignType}" VerticalAlignment="{Binding Appearance.SelectedVAlignType}"
>
<TextBlock.Style>
<Style TargetType="TextBlock">
<Style.Triggers>
<DataTrigger Binding="{Binding Appearance.IndexImage}" Value="1">
<Setter Property="RenderTransform" >
<Setter.Value>
Error here-----> <TranslateTransform X="{Binding Appearance.Offset.X}" Y="{Binding Appearance.Offset.Y}" />
</Setter.Value>
</Setter>
</DataTrigger>
</Style.Triggers>
</Style>
</TextBlock.Style>
</TextBlock>
<Path StrokeThickness="{Binding Appearance.GlyphThickness}" Stroke="{Binding Appearance.GlyphColor, Converter={StaticResource MyconverterColorToSolidColorBrush}}" >
:
:
</Path>
</Grid>
</UserControl>
ViewModel file:
namespace Cockpit.Core.Plugins.Plugins
{
[Identity(GroupName = "PushButton", Name ="", Type = typeof(PushButton_ViewModel))]
[DataContract(Name = "Cockpit.Core.Plugins.Plugins.PushButton_ViewModel")]
public class PushButton_ViewModel : PropertyChangedBase, IPluginModel
{
private readonly IEventAggregator eventAggregator;
[DataMember] public PushButtonAppearanceViewModel Appearance { get; private set; }
public PushButton_ViewModel(IEventAggregator eventAggregator, params object[] settings)
{
Appearance = new PushButtonAppearanceViewModel(settings);
NameUC = (string)settings[2];
this.eventAggregator = eventAggregator;
System.Diagnostics.Debug.WriteLine($"entree push {NameUC} {this}");
}
}
}
Appearance ViewModel:
namespace Cockpit.Core.Plugins.Plugins.Properties
{
[DataContract]
public class PushButtonAppearanceViewModel : PropertyChangedBase, IPluginProperty
{
public string NameUC { get; set; }
public PushButtonAppearanceViewModel(params object[] settings)
{
:
:
Name = "Appearance";
}
public string Name { get; set; }
private TextFormat textformat;
public TextFormat TextFormat
{
get => textformat;
set
{
textformat = value;
NotifyOfPropertyChange(() => TextFormat);
}
}
private string textPushOffset;
public string TextPushOffset
{
get => textPushOffset;
set
{
textPushOffset = value;
var a = value.Split(',').Select(i => Convert.ToInt32(i)).ToArray();
Offset = new Point(a[0], a[1]);
NotifyOfPropertyChange(() => TextPushOffset);
}
}
private Point offset;
public Point Offset
{
get => offset;
set
{
offset = value;
NotifyOfPropertyChange(() => Offset);
}
}
}
}
This might help you out.
Datatemplate binding spam Output window with error: Cannot find governing FrameworkElemen
The accepted answer says that a transform doesn't live in the visual or logical tree, so it can't inherit the data context needed to complete the binding.
The recommended solution is to define the transform as a resource of your TextBlock:
<TextBlock ...>
<TextBlock.Resources>
<TranslateTransform x:Key="MyTransform" X="{Binding Appearance.Offset.X}" Y="{Binding Appearance.Offset.Y}" />
</TextBlock.Resources>
<TextBlock.Style>
<Style TargetType="TextBlock">
<Style.Triggers>
<DataTrigger Binding="{Binding Appearance.IndexImage}" Value="1">
<Setter Property="RenderTransform" >
<Setter.Value>
<StaticResource ResourceKey="MyTransform" />
</Setter.Value>
</Setter>
</DataTrigger>
</Style.Triggers>
</Style>
</TextBlock.Style>
</TextBlock>
I hope this is helpful.
Related
I have a WPF Application in net core. I use prism and mahapps.
In MainWindow I have a Hamburger menu where I add HamburgerMenuGlyphItem via regions.
In VM for HamburgerMenuGlyphItemView, I set a label property but it doesn't work. The label property doesn't update on MenuItemControl. When I check the DataContext for the DeclarationMenuItemView in LiveVisualTree it says "DeclarationMenuItemView" not the ViewModel...
Here is the MainWindow with DataTemplate For HamburgerMenuItem
<mah:MetroWindow x:Class="PrismJPKEditor.Views.MainWindow"
xmlns:mah="http://metro.mahapps.com/winfx/xaml/controls"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:prism="http://prismlibrary.com/"
xmlns:iconPacks="http://metro.mahapps.com/winfx/xaml/iconpacks"
prism:ViewModelLocator.AutoWireViewModel="True"
xmlns:viewModels="clr-namespace:PrismJPKEditor.Modules.JPK.ViewModels;assembly=PrismJPKEditor.Modules.JPK"
xmlns:core="clr-namespace:PrismJPKEditor.Core;assembly=PrismJPKEditor.Core"
Title="{Binding Title}" Height="350" Width="525" >
<DockPanel>
<DockPanel.Resources>
<DataTemplate x:Key="MenuItemTemplate" DataType="{x:Type viewModels:MenuItemViewModel}">
<Grid x:Name="RootGrid"
Height="48"
Background="Transparent">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type mah:HamburgerMenu}}, Path=CompactPaneLength}" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<ContentControl Grid.Column="0"
HorizontalAlignment="Center"
VerticalAlignment="Center"
Content="{Binding Icon}"
Focusable="False" />
<TextBlock Grid.Column="1"
VerticalAlignment="Center"
FontSize="16"
Text="{Binding Label}"/>
</Grid>
<DataTemplate.Triggers>
<DataTrigger Binding="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type mah:HamburgerMenu}}, Path=IsPaneOpen}" Value="False">
<Setter TargetName="RootGrid" Property="ToolTip" Value="{Binding ToolTip, Mode=OneWay}" />
</DataTrigger>
</DataTemplate.Triggers>
</DataTemplate>
</DockPanel.Resources>
<Menu DockPanel.Dock="Top">
<MenuItem Header="Plik">
<MenuItem Header="Otwórz"/>
<MenuItem Header="Zapisz"/>
<MenuItem Header="Zapisz jako"/>
<MenuItem Header="Zamknij"/>
</MenuItem>
</Menu>
<mah:HamburgerMenu DockPanel.Dock="Left" ItemTemplate="{StaticResource MenuItemTemplate}" OptionsItemTemplate="{StaticResource MenuItemTemplate}">
<mah:HamburgerMenu.ItemsSource>
<mah:HamburgerMenuItemCollection prism:RegionManager.RegionName="{x:Static core:RegionNames.HamburgerMenuRegion}"/>
</mah:HamburgerMenu.ItemsSource>
</mah:HamburgerMenu>
<!--<mah:HamburgerMenu DockPanel.Dock="Left" ItemTemplate="{StaticResource MenuItemTemplate}" prism:RegionManager.RegionName="{x:Static core:RegionNames.HamburgerMenuRegion}">
</mah:HamburgerMenu>-->
<ContentControl prism:RegionManager.RegionName="{x:Static core:RegionNames.ContentRegion}"/>
</DockPanel>
</mah:MetroWindow>
Here is the MenuItemViewModel from MenuItemTemplate
public class MenuItemViewModel : BindableBase, IHamburgerMenuItemBase
{
private object _icon;
private object _label;
private object _toolTip;
private bool _isVisible = true;
public object Icon
{
get => _icon;
set => SetProperty(ref _icon, value);
}
public object Label
{
get => _label;
set => SetProperty(ref _label, value);
}
public object ToolTip
{
get => _toolTip;
set => SetProperty(ref _toolTip, value);
}
public bool IsVisible
{
get => _isVisible;
set => SetProperty(ref _isVisible, value);
}
}
And here is the ViewModel for MenuItemView when I initialize the Label property which should be working
public class DeclarationMenuItemViewModel : MenuItemViewModel
{
public DeclarationMenuItemViewModel()
{
Label = "Deklaracja";
}
}
And here is the MenuItemView
<mah:HamburgerMenuGlyphItem x:Class="PrismJPKEditor.Modules.JPK.HamburgerMenuItems.DeclarationMenuItemView"
xmlns:mah="http://metro.mahapps.com/winfx/xaml/controls"
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:PrismJPKEditor.Modules.JPK.HamburgerMenuItems"
mc:Ignorable="d"
xmlns:prism="http://prismlibrary.com/"
prism:ViewModelLocator.AutoWireViewModel="True"
>
</mah:HamburgerMenuGlyphItem>
Here is the Region adapter for the HamburgermenuItemCollection
public HamburgerMenuItemCollectionRegionAdapter(IRegionBehaviorFactory regionBehaviorFactory) : base(regionBehaviorFactory)
{
}
protected override void Adapt(IRegion region, HamburgerMenuItemCollection regionTarget)
{
region.Views.CollectionChanged += (s, e) =>
{
if (e.Action == System.Collections.Specialized.NotifyCollectionChangedAction.Add)
{
foreach (HamburgerMenuGlyphItem element in e.NewItems)
{
regionTarget.Add(element);
}
}
};
}
protected override IRegion CreateRegion()
{
return new SingleActiveRegion();
}
}
Because the View DeclarationMenuItemView isn't in the Views folder, I have set
public void RegisterTypes(IContainerRegistry containerRegistry)
{
ViewModelLocationProvider.Register<DeclarationMenuItemView, DeclarationMenuItemViewModel>();
}
Here is the Module class
public class JPKModule : IModule
{
private readonly IRegionManager _regionManager;
public JPKModule(IRegionManager regionManager)
{
_regionManager = regionManager;
}
public void OnInitialized(IContainerProvider containerProvider)
{
_regionManager.RegisterViewWithRegion(RegionNames.HamburgerMenuRegion, typeof(HamburgerMenuItems.DeclarationMenuItemView));
//_regionManager.RegisterViewWithRegion(RegionNames.HamburgerMenuRegion, typeof(HamburgerMenuItems.SellMenuItem));
//_regionManager.RegisterViewWithRegion(RegionNames.HamburgerMenuRegion, typeof(HamburgerMenuItems.BuyMenuItem));
//_regionManager.RegisterViewWithRegion(RegionNames.HamburgerMenuRegion, typeof(TestView));
}
public void RegisterTypes(IContainerRegistry containerRegistry)
{
ViewModelLocationProvider.Register<DeclarationMenuItemView, DeclarationMenuItemViewModel>();
}
}
For some testing I changed the DataTemplate to
<DataTemplate x:Key="HamburgerMenuItem" DataType="{x:Type mah:HamburgerMenuGlyphItem}">
<DockPanel Height="48" LastChildFill="True">
<Grid x:Name="IconPart"
Width="{Binding RelativeSource={RelativeSource AncestorType={x:Type mah:HamburgerMenu}}, Path=CompactPaneLength}"
DockPanel.Dock="Left">
<Image Margin="12"
HorizontalAlignment="Center"
VerticalAlignment="Center"
Source="{Binding Glyph}" />
</Grid>
<TextBlock x:Name="TextPart"
VerticalAlignment="Center"
FontSize="16"
Text="{Binding Label}" />
</DockPanel>
<DataTemplate.Triggers>
<DataTrigger Binding="{Binding RelativeSource={RelativeSource AncestorType={x:Type mah:HamburgerMenu}}, Path=PanePlacement}" Value="Right">
<Setter TargetName="IconPart" Property="DockPanel.Dock" Value="Right" />
<Setter TargetName="TextPart" Property="Margin" Value="8 0 0 0" />
</DataTrigger>
</DataTemplate.Triggers>
</DataTemplate>
And in the cs file of the View I set
public partial class DeclarationMenuItem : HamburgerMenuGlyphItem
{
public DeclarationMenuItem()
{
InitializeComponent();
Glyph = "M";
Label = "Test";
}
}
And from here Label is working ..
And when I add to the xaml file of the view the line
Label = "{Binding Label"}
I get following error:
Error 1 MainWindowViewModel Label DeclarationMenuItemView.Label String Label property not found on object of type MainWindowViewModel.
But VM for DeclarationMenuItem is DeclarationMenuItemViewModel not the
MainWindowViewModel
I have a WPF window with a listview with textboxes in it, and I want to enable users to switch between textboxes with the TAB key. I created a function that does that, but every time the textbox loses focus, I refresh the listview and so the listview itself gets focused.
I know the problem occurs because of the refresh event because commenting that part will result in the correct element (the textbox) being focused.
I have tried many alternative solutions but none works (prevent item losing focus when refreshing list view in c#; Focus on TextBox after ListView 'SelectionChanged' Event).
It seems there is something problematic in focusing on an that element after the listview is refreshed.
I have tried remembering the index of the item and then focusing on it, after the TAB key was pressed. tried also remembering the focused control and then focusing on it after the refresh.
private void RTB_Reference_LostFocus(object sender, RoutedEventArgs e)
{
int Index = DetailsList.SelectedIndex;
Index = DetailsList.Items.IndexOf(DetailsList.SelectedItem);
try
{
//Get cell value by using sender Object
string inTime = ((System.Windows.Controls.TextBox)sender).Text;
DetailItem item = (DetailItem)DetailsList.Items[Index];
item.Reference = inTime;
UpdateExplanation(item);
}
catch (Exception)
{
}
}
private void RTB_Detail_KeyDown(object sender, System.Windows.Input.KeyEventArgs e)
{
if (e.Key == Key.Tab)
{
e.Handled = true;
//System.Windows.MessageBox.Show("Tab");
int Idx = DetailsList.SelectedIndex;
System.Windows.Controls.ListViewItem lvi = (System.Windows.Controls.ListViewItem)DetailsList.ItemContainerGenerator.ContainerFromItem(DetailsList.SelectedItem);
GUF.FocusItem(DetailsList, Idx, "RTB_Detail");
//IsLastKeyTAB = true;
}
//else
// IsLastKeyTAB = false;
}
private void UpdateExplanation(DetailItem item)
{
item.Explanation = GetExplanation(item.Reference, item.Detail);
IInputElement focusedControl = Keyboard.FocusedElement;
DetailsList.Items.Refresh();
focusedControl.Focus();
RefreshDetailsList(DetailsList, IsEnglish);
}
expected result is to keep focus on that textbox after the refresh. that does not occur...
EDIT 1
This is the listview xaml:
<ListView FlowDirection="RightToLeft" x:Name="DetailsList" VirtualizingStackPanel.IsVirtualizing="False"
HorizontalAlignment="Stretch" VerticalAlignment="Top"
HorizontalContentAlignment="Center" ScrollViewer.VerticalScrollBarVisibility="Visible" Padding="0 0 0 25"
AllowDrop="True"
ItemsSource="{Binding DetailItem}"
Loaded="ListView_Loaded"
Margin="26,157,23,0"
dd:DragDrop.IsDragSource="True"
dd:DragDrop.IsDropTarget="True"
dd:DragDrop.DropHandler="{Binding}" Height="599">
<ListView.ItemContainerStyle>
<Style TargetType="{x:Type ListViewItem}">
<Setter Property="BorderThickness" Value="0" />
<Setter Property="Foreground" Value="Black" />
<Setter Property="Margin" Value="4, 4, 4, 4"/>
<Setter Property="FontWeight" Value="DemiBold"/>
<Setter Property="Height" Value="22"/>
<Setter Property="HorizontalContentAlignment" Value="Stretch"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ListViewItem}">
<Border BorderBrush="Transparent" BorderThickness="0" Background="{TemplateBinding Background}">
<GridViewRowPresenter HorizontalAlignment="Stretch" VerticalAlignment="{TemplateBinding VerticalContentAlignment}" Width="Auto" Margin="0" Content="{TemplateBinding Content}"/>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<!-- <Setter Property="Background" Value="#6B54FF"/> -->
<Setter Property="Foreground" Value="#6B57FF"/>
<Setter Property="BorderThickness" Value="2" />
</Trigger>
<Trigger Property="IsSelected" Value="True">
<Setter Property="BorderThickness" Value="2" />
<Setter Property="BorderBrush" Value="#6B57FF"/>
<Setter Property="Foreground" Value="#6B57FF" />
</Trigger>
</Style.Triggers>
</Style>
</ListView.ItemContainerStyle>
<ListView.View>
<GridView>
<GridViewColumn Width="50" Header="סימון" DisplayMemberBinding="{Binding Mark}"/>
<!--
<GridViewColumn Width="30" >
<GridViewColumn.CellTemplate>
<DataTemplate >
<Button Style="{StaticResource PlusButtonStyle}" x:Name="buttonPlusDocument" Click="buttonPlusDocument_Click" />
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
-->
<GridViewColumn Header="הפניה במסמך" Width="150">
<GridViewColumn.CellTemplate>
<DataTemplate>
<TextBox x:Name="RTB_Reference" BorderBrush="#5f27ff" BorderThickness="1" KeyDown="RTB_Reference_KeyDown" HorizontalAlignment="Stretch" Height="20" Margin="0" Padding="0" FontSize="12" IsEnabled="True"
LostFocus="RTB_Reference_LostFocus" GotFocus="RTB_Reference_GotFocus">
<TextBox.Resources>
<Style TargetType="{x:Type Border}">
<Setter Property="CornerRadius" Value="2"/>
<Setter Property="BorderBrush" Value="#5f27ff"/>
<Setter Property="BorderThickness" Value="1" />
</Style>
</TextBox.Resources>
</TextBox>
<!--DataContext="{Binding SelectedItem, ElementName=ListViewAppendixNameList, Mode=TwoWay}"-->
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
<GridViewColumn Header="פרט" Width="150">
<GridViewColumn.CellTemplate>
<DataTemplate>
<TextBox x:Name="RTB_Detail" BorderBrush="#5f27ff" BorderThickness="1" HorizontalAlignment="Stretch" Height="20" Margin="0" Padding="0" FontSize="12" IsEnabled="True"
KeyDown="RTB_Detail_KeyDown" LostFocus="RTB_Detail_LostFocus" GotFocus="RTB_Detail_GotFocus">
<TextBox.Resources>
<Style TargetType="{x:Type Border}">
<Setter Property="CornerRadius" Value="2"/>
<Setter Property="BorderBrush" Value="#5f27ff"/>
<Setter Property="BorderThickness" Value="1" />
</Style>
</TextBox.Resources>
</TextBox>
<!--DataContext="{Binding SelectedItem, ElementName=ListViewAppendixNameList, Mode=TwoWay}"-->
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
<GridViewColumn Header="הסבר" Width="350" DisplayMemberBinding="{Binding Explanation}"/>
<GridViewColumn Width="30" >
<GridViewColumn.CellTemplate>
<DataTemplate >
<Button Style="{StaticResource DeleteButtonStyle}" x:Name="BT_DeleteDetail" Click="BT_DeleteDetail_Click" />
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
</GridView>
</ListView.View>
</ListView>
This is the details class:
public class DetailItem
{
public string Mark { get; set; }
public string Reference { get; set; }
public string Detail { get; set; }
public string Explanation { get; set; }
}
I refresh the listview so that the Explanation text will be updated.
I don't went through your whole code but only those parts that are relevant to your question. To simplify things, I will use the attached property TabNavigation and the data binding feature. The binding will automatically update the control values so that the refreshing of the ListView and the focus handling becomes redundant.
Remark: To shorten the XAML code I will only show the relevant code portions.
Tab navigation
Set the attached property KeyboardNavigation.TabNavigation on the ListView to Continue so that the focus will loop through the list:
<ListView x:Name="DetailsList"
KeyboardNavigation.TabNavigation="Continue"
...
</ListView>
You can control which elements can receive focus on Tab key pressed by setting the property IsTabStop on the elements. The default value is True. So set it to False on those elements you wish to exclude. It's useful to disable Tab focus on the ListViewItem itself so that the Tab key is only effective for the containing controls:
<ListView.ItemContainerStyle>
<Style TargetType="{x:Type ListViewItem}">
<Setter Property="IsTabStop"
Value="False" />
...
</Style>
</ListView.ItemContainerStyle>
If you wish to exclude the Button as well, so that you only jump between the TextBox elements, set the Button's IsTabStopproperty to False as well:
<GridViewColumn Width="30">
<GridViewColumn.CellTemplate>
<DataTemplate>
<Button x:Name="BT_DeleteDetail"
IsTabStop="False"
...
/>
...
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
If you also wish to change the order the elements will receive focus on Tab key pressed, you can do it by setting the element's TabIndex property. A control with a lower tab index receives focus before a control with a higher index.
Binding the TextBox
To enable binding to a data model it is required that the data model (view model) implements INotifyPropertxChanged:
public class DetailItem : INotifyPropertyChanged
{
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
public event PropertyChangedEventHandler PropertyChanged;
private string mark;
public string Mark
{
get => this.mark;
set
{
if (value == this.mark) return;
this.mark = value;
OnPropertyChanged();
}
}
private string reference;
public string Reference
{
get => this.reference;
set
{
if (value == this.reference) return;
this.reference = value;
OnPropertyChanged();
}
}
private string detail;
public string Detail
{
get => this.detail;
set
{
if (value == this.detail) return;
this.detail = value;
OnPropertyChanged();
}
}
private string explanation;
public string Explanation
{
get => this.explanation;
set
{
if (value == this.explanation) return;
this.explanation = value;
OnPropertyChanged();
}
}
}
In order access the view model from your DataTemplate set its DataType property to the type of the view model (DetailItem). Remember to always set the DataType of a DataTemplate. Then add the Binding to the TextBox. Note that the Mode (direction) of the binding is set to OneWayToSource. This limits the binding to set data from TextBoxto DetailItem only:
<GridViewColumn.CellTemplate>
<DataTemplate DataType="viewModels:DetailItem">
<TextBox x:Name="RTB_Reference"
Text="{Binding Reference, Mode=OneWayToSource}"
...
</TextBox>
...
</DataTemplate>
</GridViewColumn.CellTemplate>
<GridViewColumn.CellTemplate>
<DataTemplate DataType="viewModels:DetailItem">
<TextBox x:Name="RTB_Detail"
Text="{Binding Detail, Mode=OneWayToSource}"
...
</TextBox>
...
</DataTemplate>
</GridViewColumn.CellTemplate>
The final step is to update the DetailItem.Explanation property. To do this, we move the UpdateExplanation() method into the view model DetailItem. Since we use binding we can now get rid of all the ListView refresh and focus logic inside, so the method becomes smaller. Note that since we moved the method into the DetailItem class, we can also remove the parameter of the method:
private void UpdateExplanation()
{
this.Explanation = GetExplanation(this.Reference, this.Detail);
}
The UpdateExplanation() method is invoked directly from the setter of the Reference and Detail property, so that everytime these properties are changed the Explanation value will be updated. With the updated Reference and Detail setter the final version of the DetailItem class will then look as followed:
public class DetailItem : INotifyPropertyChanged
{
private void UpdateExplanation()
{
this.Explanation = GetExplanation(this.Reference, this.Detail);
}
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
public event PropertyChangedEventHandler PropertyChanged;
private string mark;
public string Mark
{
get => this.mark;
set
{
if (value == this.mark) return;
this.mark = value;
OnPropertyChanged();
}
}
private string reference;
public string Reference
{
get => this.reference;
set
{
if (value == this.reference) return;
this.reference = value;
OnPropertyChanged();
UpdateExplanation();
}
}
private string detail;
public string Detail
{
get => this.detail;
set
{
if (value == this.detail) return;
this.detail = value;
OnPropertyChanged();
UpdateExplanation();
}
}
private string explanation;
public string Explanation
{
get => this.explanation;
set
{
if (value == this.explanation) return;
this.explanation = value;
OnPropertyChanged();
}
}
}
Try Below Code. Enjoy !!!
<Grid>
<ListView Margin="45.5,47.5,41.167,0" Name="lvtestListview" Height="188.333" VerticalAlignment="Top">
<ListView.ItemContainerStyle>
<Style TargetType="ListViewItem">
<Setter Property="HorizontalContentAlignment" Value="Stretch"/>
<Setter Property="VerticalContentAlignment" Value="Stretch"/>
<!--<Setter Property="IsSelected" Value="{Binding IsGroovy}"/>-->
<Style.Triggers>
<Trigger Property="IsSelected" Value="true" >
<Setter Property="Foreground" Value="Blue" />
<Setter Property="Background" Value="LightGreen" />
</Trigger>
</Style.Triggers>
</Style>
</ListView.ItemContainerStyle>
<ListView.View>
<GridView>
<GridViewColumn Header="Item Code" Width="100">
<GridViewColumn.CellTemplate>
<DataTemplate>
<Border BorderBrush="Gray" BorderThickness=".5" Margin="-6,-3">
<TextBlock Text="{Binding ItemCode}" Margin="6,3"/>
</Border>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
<GridViewColumn Header="Item Name" Width="100">
<GridViewColumn.CellTemplate>
<DataTemplate>
<Border BorderBrush="Gray" BorderThickness=".5" Margin="-6,-3">
<TextBox Loaded="txtBarcode_Loaded" Text="{Binding ItemName}" Name="txtBarcode" Focusable="{Binding IsFocused}" Cursor="IBeam" />
</Border>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
<GridViewColumn Header="Next Item" Width="100">
<GridViewColumn.CellTemplate>
<DataTemplate>
<Border BorderBrush="Gray" BorderThickness=".5" Margin="-6,-3">
<TextBox TabIndex="3" Name="txtgvPL1" KeyDown="txtgvPL1_KeyDown" Text="{Binding PL1}" Cursor="IBeam"/>
</Border>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
</GridView>
</ListView.View>
</ListView>
</Grid>
code behind
public class ClassA : INotifyPropertyChanged
{
public string ItemCode { get; set; }
public string ItemName { get; set; }
private bool isFocused;
public event PropertyChangedEventHandler PropertyChanged;
public bool IsFocused
{
get
{
return isFocused;
}
set
{
if (value != isFocused)
{
isFocused = value;
RaisePropertyChanged("IsFocused");
}
}
}
private void RaisePropertyChanged(string propName)
{
PropertyChangedEventHandler eh = PropertyChanged;
if (eh != null)
{
eh(this, new PropertyChangedEventArgs(propName));
}
}
}
public partial class MainWindow : Window
{
List<ClassA> lstClassA = new List<ClassA>();
public MainWindow()
{
InitializeComponent();
BindGrid();
}
private void BindGrid()
{
ClassA ca = new ClassA();
ca.ItemCode = "A";
ca.IsFocused = true;
lstClassA.Add(ca);
lvtestListview.ItemsSource = lstClassA;
}
private void txtBarcode_Loaded(object sender, RoutedEventArgs e)
{
Keyboard.Focus(((TextBox)sender));
}
private void txtgvPL1_KeyDown(object sender, KeyEventArgs e)
{
if (e.Key == Key.Enter )
{
ClassA ca = new ClassA();
ca.ItemCode = "B";
ca.IsFocused = true;
lstClassA.Add(ca);
foreach (ClassA a in lstClassA)
{
if (a.ItemCode == "A")
{
a.IsFocused = false;
}
}
lvtestListview.ItemsSource = lstClassA;
lvtestListview.Items.Refresh();
}
}
}
I have a canvas, and I want to give it grid lines as a background, but I want there to be a constant number of grid lines that divide the canvas into equally-sized sections, rather than just have equally-spaced grid-lines. I want this to be preserved when the canvas is resized by the user.
How should I do this?
Here is a solution which is based in two wpf ListView controls behind the canvas(one for rows and second for columns). The content of the columns related ListView control is a rectangle.
Updated version - Managed Grid Lines Control. Here you can manage the number of grid lines and their visibility.
Xaml code - grid lines control:
<UserControl x:Class="CAnvasWithGrid.GridLineControl"
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:canvasWithGrid="clr-namespace:CAnvasWithGrid"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300" x:Name="This">
<Grid x:Name="LayoutRoot">
<Grid.Resources>
<Style TargetType="ListView">
<Setter Property="Background" Value="Transparent"/>
</Style>
<Style x:Key="ListViewItemStyle" TargetType="ListViewItem">
<Setter Property="Background" Value="Transparent"/>
</Style>
<DataTemplate x:Key="InnerListviewDataTemplate" DataType="{x:Type canvasWithGrid:CellModel}">
<Rectangle HorizontalAlignment="Stretch" VerticalAlignment="Stretch"
Margin="0" StrokeDashArray="4" Stroke="Black" StrokeThickness="0.5" Fill="Transparent"/>
</DataTemplate>
<DataTemplate x:Key="ListviewDataTemplate" DataType="{x:Type canvasWithGrid:RowModel}">
<ListView ItemsSource="{Binding CellModels}" BorderBrush="#00FFFFFF" BorderThickness="0" Margin="0"
HorizontalContentAlignment="Stretch"
VerticalContentAlignment="Stretch"
VerticalAlignment="Stretch"
HorizontalAlignment="Stretch" ScrollViewer.HorizontalScrollBarVisibility="Hidden" ScrollViewer.VerticalScrollBarVisibility="Hidden">
<ListView.ItemsPanel>
<ItemsPanelTemplate>
<UniformGrid Columns="{Binding CellModels, Converter={canvasWithGrid:CollectionLength2NumberConverter}}"></UniformGrid>
</ItemsPanelTemplate>
</ListView.ItemsPanel>
<ListView.ItemContainerStyle>
<Style TargetType="ListViewItem" BasedOn="{StaticResource ListViewItemStyle}">
<Setter Property="Margin" Value="0"></Setter>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ListViewItem">
<ContentPresenter Content="{TemplateBinding Content}" Margin="0"
ContentTemplate="{StaticResource InnerListviewDataTemplate}" />
</ControlTemplate>
</Setter.Value>
</Setter>
<Setter Property="ContentTemplate" Value="{StaticResource InnerListviewDataTemplate}"/>
</Style>
</ListView.ItemContainerStyle>
</ListView>
</DataTemplate>
</Grid.Resources>
<ListView ItemsSource="{Binding ElementName=This, Path=RowModels}" ScrollViewer.HorizontalScrollBarVisibility="Hidden" ScrollViewer.VerticalScrollBarVisibility="Hidden">
<ListView.ItemsPanel>
<ItemsPanelTemplate>
<UniformGrid Rows="{Binding ElementName=This, Path=RowModels, Converter={canvasWithGrid:CollectionLength2NumberConverter}}"/>
</ItemsPanelTemplate>
</ListView.ItemsPanel>
<ListView.ItemContainerStyle>
<Style TargetType="ListViewItem" BasedOn="{StaticResource ListViewItemStyle}">
<Setter Property="Margin" Value="0"></Setter>
<Setter Property="HorizontalAlignment" Value="Stretch"/>
<Setter Property="VerticalAlignment" Value="Stretch"/>
<Setter Property="HorizontalContentAlignment" Value="Stretch"/>
<Setter Property="VerticalContentAlignment" Value="Stretch"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ListViewItem">
<ContentPresenter Content="{TemplateBinding Content}" Margin="-1"
ContentTemplate="{StaticResource ListviewDataTemplate}" />
</ControlTemplate>
</Setter.Value>
</Setter>
<Setter Property="ContentTemplate" Value="{StaticResource ListviewDataTemplate}"/>
</Style>
</ListView.ItemContainerStyle>
</ListView>
</Grid>
Grid lines control - code behind
/// <summary>
/// Interaction logic for GridLineControl.xaml
/// </summary>
public partial class GridLineControl : UserControl
{
public GridLineControl()
{
InitializeComponent();
}
public static readonly DependencyProperty NumberOfColumnsProperty = DependencyProperty.Register(
"NumberOfColumns", typeof (int), typeof (GridLineControl), new PropertyMetadata(default(int), NumberOfColumnsChangedCallback));
private static void NumberOfColumnsChangedCallback(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs args)
{
var numberOfRows = (int)dependencyObject.GetValue(NumberOfRowsProperty);
var numberOfColumns = (int)args.NewValue;
if (numberOfColumns == 0 || numberOfRows == 0) return;
var rowModelsCollection = GetRowModelsCollection(numberOfRows, numberOfColumns);
dependencyObject.SetValue(RowModelsProperty, rowModelsCollection);
}
public int NumberOfColumns
{
get { return (int) GetValue(NumberOfColumnsProperty); }
set { SetValue(NumberOfColumnsProperty, value); }
}
public static readonly DependencyProperty NumberOfRowsProperty = DependencyProperty.Register(
"NumberOfRows", typeof (int), typeof (GridLineControl), new PropertyMetadata(default(int), NumberOfRowsChangedCallback));
private static void NumberOfRowsChangedCallback(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs args)
{
var numberOfRows = (int)args.NewValue;
var numberOfColumns = (int)dependencyObject.GetValue(NumberOfColumnsProperty);
if(numberOfColumns == 0 || numberOfRows == 0) return;
var rowModelsCollection = GetRowModelsCollection(numberOfRows, numberOfColumns);
dependencyObject.SetValue(RowModelsProperty, rowModelsCollection);
}
private static ObservableCollection<RowModel> GetRowModelsCollection(int numberOfRows, int numberOfColumns)
{
var rowModelsCollection = new ObservableCollection<RowModel>();
for (var i = 0; i < numberOfRows; i++)
{
rowModelsCollection.Add(new RowModel(numberOfColumns) {Position = (i + 1).ToString()});
}
return rowModelsCollection;
}
public int NumberOfRows
{
get { return (int) GetValue(NumberOfRowsProperty); }
set { SetValue(NumberOfRowsProperty, value); }
}
public static readonly DependencyProperty RowModelsProperty = DependencyProperty.Register("RowModels",
typeof(ObservableCollection<RowModel>), typeof(GridLineControl),
new PropertyMetadata(default(ObservableCollection<RowModel>)));
public ObservableCollection<RowModel> RowModels
{
get { return (ObservableCollection<RowModel>)GetValue(RowModelsProperty); }
private set { SetValue(RowModelsProperty, value); }
}
}
Models:
public class RowModel:BaseGridMember
{
public RowModel(int numberOfCellsInRow)
{
CellModels = new ObservableCollection<CellModel>();
for (int i = 0; i < numberOfCellsInRow; i++)
{
CellModels.Add(new CellModel{Position = (i+1).ToString()});
}
}
public ObservableCollection<CellModel> CellModels { get; set; }
}
public class CellModel:BaseGridMember
{
}
public class BaseGridMember:BaseObservableObject
{
private string _position;
public string Position
{
get { return _position; }
set
{
_position = value;
OnPropertyChanged();
}
}
}
Main window xaml code - as you can see here is a ImageContol instead of Canvas but you can replace it:
<Window x:Class="CAnvasWithGrid.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:canvasWithGrid="clr-namespace:CAnvasWithGrid"
Title="MainWindow" Height="525" Width="525" x:Name="This">
<Grid Tag="{Binding ElementName=This}">
<Grid.Resources>
<BooleanToVisibilityConverter x:Key="Bool2VisConvKey" />
</Grid.Resources>
<Grid.ContextMenu>
<ContextMenu DataContext="{Binding Path=PlacementTarget.Tag, RelativeSource={RelativeSource Self}}">
<MenuItem Header="Show Grid Lines" Command="{Binding ShowGridLinesCommand}"/>
</ContextMenu>
</Grid.ContextMenu>
<Image Source="Resources/Koala.jpg" Stretch="Uniform" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" MouseDown="UIElement_OnMouseDown"/>
<canvasWithGrid:GridLineControl NumberOfRows="50" NumberOfColumns="50"
IsHitTestVisible="False" Visibility="{Binding ElementName=This, Path=AreGridLineVisible, Converter={StaticResource Bool2VisConvKey}, UpdateSourceTrigger=PropertyChanged}"/>
</Grid>
Main window code behind:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
ShowGridLinesCommand = new RelayCommand(ShowGridLineManageCommand);
AreGridLineVisible = true;
}
private void ShowGridLineManageCommand()
{
AreGridLineVisible = !AreGridLineVisible;
}
public static readonly DependencyProperty AreGridLineVisibleProperty = DependencyProperty.Register(
"AreGridLineVisible", typeof (bool), typeof (MainWindow), new PropertyMetadata(default(bool)));
public bool AreGridLineVisible
{
get { return (bool) GetValue(AreGridLineVisibleProperty); }
set { SetValue(AreGridLineVisibleProperty, value); }
}
public static readonly DependencyProperty ShowGridLinesCommandProperty = DependencyProperty.Register(
"ShowGridLinesCommand", typeof (ICommand), typeof (MainWindow), new PropertyMetadata(default(ICommand)));
public ICommand ShowGridLinesCommand
{
get { return (ICommand) GetValue(ShowGridLinesCommandProperty); }
set { SetValue(ShowGridLinesCommandProperty, value); }
}
private void UIElement_OnMouseDown(object sender, MouseButtonEventArgs e)
{
}
}
How it looks like:
Sounds like a candidate for a custom control with custom drawing. You don't really want to use multiple FrameworkElements like "Line" if you are expecting many grid-lines for performance reasons.
So you'd create a customControl GridLinesControl and overwrite the OnRender method. You can get the actual width and height of the component using the properties ActualWidth and ActualHeight, divide by the number of grid lines you want and draw lines using drawingContext.DrawLine.
The easiest way would be to add the GridLinesControl you've made underneath the canvas, taking up the same space (so it has the right ActualWidth and ActualHeight) like this:
<Grid>
<myControls:GridLinesControl/>
<Canvas ... />
</Grid>
So it's always underneath.
I have got a little problem.
I use ListBox control with the textboxes.
I set focus on the first textbox and try to jump on the following textbox by the key tab.
It does not work.
What do I wrong?
Thanks in advance!
<ListBox Name="Box" ScrollViewer.HorizontalScrollBarVisibility="Disabled" Background="Transparent" BorderThickness="0">
<ListBox.ItemContainerStyle>
<Style TargetType="ListBoxItem">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate>
<StackPanel Orientation="Horizontal" Margin="40,2,0,2">
<TextBlock Text="{Binding Label}" MinWidth="20" />
<TextBox TabIndex="{Binding Index, Mode=OneWay}" Text="{Binding Information, Mode=TwoWay}"/>
</StackPanel>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ListBox.ItemContainerStyle>
</ListBox>
namespace SilverlightApplication1
{
public partial class MainPage : UserControl
{
public MainPage()
{
InitializeComponent();
var model = new List<Model>()
{
new Model() {Index = 1, Label = "1"},
new Model() {Index = 2, Label = "2"},
new Model() {Index = 3, Label = "3"},
new Model() {Index = 4, Label = "4"}
};
Box.ItemsSource = model;
}
}
public class Model
{
public int Index { get; set; }
public string Label { get; set; }
public string Information { get; set; }
}
}
You'll need to specify in the style how you want your tabs to work. You shouldn't need to bind the tabindex, unless you want to change up the order the tab works in. I think this should work similar to what you are trying to do:
<ListBox Name="Box"
ScrollViewer.HorizontalScrollBarVisibility="Disabled"
Background="Transparent"
BorderThickness="0">
<ListBox.ItemContainerStyle>
<Style TargetType="ListBoxItem">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate>
<StackPanel Orientation="Horizontal"
Margin="40,2,0,2">
<TextBlock Text="{Binding Label}"
MinWidth="20" />
<TextBox Text="{Binding Information, Mode=TwoWay}" />
</StackPanel>
</ControlTemplate>
</Setter.Value>
</Setter>
<Setter Property="IsTabStop"
Value="False" />
</Style>
</ListBox.ItemContainerStyle>
<ListBox.Style>
<Style TargetType="ListBox">
<Setter Property="TabNavigation"
Value="Cycle" />
</Style>
</ListBox.Style>
</ListBox>
I'm getting the following Binding errors on my code and I don't know how to troubleshoot them. The bindings were generated by VS. I've tried adding presentation.tracesources (which is in the code below) but I get the same output as before.
System.Windows.Data Error: 4 : Cannot find source for binding with reference 'RelativeSource FindAncestor, AncestorType='ClimateSolutions.SuperTB', AncestorLevel='1''. BindingExpression:Path=myName; DataItem=null; target element is 'TextBlock' (Name=''); target property is 'Text' (type 'String')
System.Windows.Data Error: 4 : Cannot find source for binding with reference 'RelativeSource FindAncestor, AncestorType='ClimateSolutions.SuperTB', AncestorLevel='1''. BindingExpression:Path=isRequired; DataItem=null; target element is 'SuperTB' (Name='email'); target property is 'NoTarget' (type 'Object')
Here's my XAML:
<TextBox x:Class="ClimateSolutions.SuperTB"
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="53" Width="296" FontSize="32"
xmlns:local="clr-namespace:ClimateSolutions"
xmlns:diagnostics="clr-namespace:System.Diagnostics;assembly=WindowsBase"
HorizontalAlignment="Left" Name="Blarg">
<TextBox.Style>
<Style TargetType="TextBox">
<Style.Triggers>
<Trigger Property="Text" Value="">
<Setter Property="Background">
<Setter.Value>
<VisualBrush Stretch="None">
<VisualBrush.Visual>
<TextBlock Foreground="Gray" FontSize="24" Text="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType=local:SuperTB, AncestorLevel=1}, Path=myName, diagnostics:PresentationTraceSources.TraceLevel=High}">
</TextBlock>
</VisualBrush.Visual>
</VisualBrush>
</Setter.Value>
</Setter>
</Trigger>
<DataTrigger Binding="{Binding Path=isRequired, RelativeSource={RelativeSource FindAncestor, AncestorType=local:SuperTB, AncestorLevel=1}}" Value="False">
<Setter Property="Text" Value="100" />
</DataTrigger>
</Style.Triggers>
</Style>
</TextBox.Style>
and here's the CS for SuperTB:
namespace ClimateSolutions
{
/// <summary>
/// Interaction logic for SuperTB.xaml
/// </summary>
public partial class SuperTB : TextBox, INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged(String Property)
{
var anEvent = this.PropertyChanged;
if (anEvent != null)
{
anEvent(this, new PropertyChangedEventArgs(Property));
}
}
private String MyName = "Unicorns!";
private static DependencyProperty myNameProperty = DependencyProperty.Register("myName", typeof(String), typeof(SuperTB));
public String myName
{
get { return MyName; }
set { MyName = value; NotifyPropertyChanged("myName"); }
}
DependencyProperty isRequiredProperty = DependencyProperty.Register("isRequired", typeof(Boolean), typeof(SuperTB));
public Boolean isRequired
{
get { return (Boolean)GetValue(isRequiredProperty); }
set { SetValue(isRequiredProperty, value); }
}
public SuperTB()
{
InitializeComponent();
myName = "Unicorns!";
}
}
}
EDIT : I have updated the code according to your comment. To summarize, since this is a custom control, you are less dependant on the MVVM pattern to build your component logic (and thus use code behind in you component) as soon as your componennt itself meets this needs (to be sort, make its properties to be as much bindable as you can). For example, in the updated code, you can now bind the default property, but you can also imagine exposing properties to set the foreground colors used to diaplay control name when there is no value, and so forth.
I tried several things with you original code (included solution provided by J cooper) and nothing seemed to work. It seems that there is a lot of issues with your code.
I managed to approach a solution by making your textbox a custom control.
Here is the Generic.xaml (the visual definition of your control) :
<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:Question_6514447">
<Style TargetType="{x:Type local:SuperTB2}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type local:SuperTB2}">
<Border Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}">
<TextBox x:Name="PART_Input">
<TextBox.Style>
<Style TargetType="TextBox">
<Style.Triggers>
<DataTrigger Binding="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=IsRequired}" Value="False">
<Setter Property="Text" Value="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=DefaultTextValue}" />
</DataTrigger>
</Style.Triggers>
</Style>
</TextBox.Style>
</TextBox>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ResourceDictionary>
And here is the code behind of the control :
[TemplatePart(Name = "PART_Input")]
public class SuperTB2 : Control
{
private TextBox PART_Input;
static SuperTB2()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(SuperTB2), new FrameworkPropertyMetadata(typeof(SuperTB2)));
}
public SuperTB2()
{
Loaded += SuperTb2Loaded;
}
public override void OnApplyTemplate()
{
PART_Input = GetTemplateChild("PART_Input") as TextBox;
if (PART_Input != null)
{
PART_Input.GotFocus += PartInputGotFocus;
PART_Input.LostFocus += PartInputLostFocus;
}
}
void PartInputLostFocus(object sender, RoutedEventArgs e)
{
if (PART_Input.Text == string.Empty)
{
PART_Input.Text = Name;
PART_Input.Foreground = new SolidColorBrush(Colors.Gray);
}
}
void PartInputGotFocus(object sender, RoutedEventArgs e)
{
if (PART_Input.Text.Equals(Name))
{
PART_Input.Text = string.Empty;
PART_Input.Foreground = new SolidColorBrush(Colors.Black);
}
}
void SuperTb2Loaded(object sender, RoutedEventArgs e)
{
if (PART_Input.Text == string.Empty)
{
PART_Input.Text = Name;
PART_Input.Foreground = new SolidColorBrush(Colors.Gray);
}
}
private static DependencyProperty myNameProperty =
DependencyProperty.Register("MyName", typeof(string), typeof(SuperTB2), new PropertyMetadata("Unicorns !", NameChanged));
private static void NameChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
}
public string MyName
{
get { return (string)GetValue(myNameProperty); }
set { SetValue(myNameProperty, value); }
}
DependencyProperty isRequiredProperty =
DependencyProperty.Register("IsRequired", typeof(bool), typeof(SuperTB2), new PropertyMetadata(false, IsReqChanged));
private static void IsReqChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
}
public bool IsRequired
{
get { return (bool)GetValue(isRequiredProperty); }
set { SetValue(isRequiredProperty, value); }
}
public string DefaultTextValue
{
get { return (string)GetValue(DefaultTextValueProperty); }
set { SetValue(DefaultTextValueProperty, value); }
}
// Using a DependencyProperty as the backing store for DefaultTextValue. This enables animation, styling, binding, etc...
public static readonly DependencyProperty DefaultTextValueProperty =
DependencyProperty.Register("DefaultTextValue", typeof(string), typeof(SuperTB2), new UIPropertyMetadata("100"));
}
And an example of use of the component :
<Grid>
<StackPanel>
<Question_6514447:SuperTB2 x:Name="FirstName" IsRequired="true" DefaultTextValue="200"/>
</StackPanel>
</Grid>
With this updated code, I think you can acheive almost all the behaviors your needed !
Hope this will help !
Do not use relative source in your binding expressions. Relative source is used to access elements higher in the element tree. It seems as though you were using it in terms of object inheritance.
<Trigger Property="Text" Value="">
<Setter Property="Background">
<Setter.Value>
<VisualBrush Stretch="None">
<VisualBrush.Visual>
<TextBlock Foreground="Gray" FontSize="24" Text="{Binding Path=myName, diagnostics:PresentationTraceSources.TraceLevel=High}">
</TextBlock>
</VisualBrush.Visual>
</VisualBrush>
</Setter.Value>
</Setter>
</Trigger>
<DataTrigger Binding="{Binding Path=isRequired}" Value="False">
<Setter Property="Text" Value="100" />
</DataTrigger>