wp7 TemplateBinding Content not working - c#

I am new in working with wpf and currently I am trying to do the following: I have created a simple ContenctControl (CtrpushPinContent) that contains a TextBlock:
<ContentControl x:Class="CtrpushPinContent" ...
<Grid x:Name="LayoutRoot" Background="{x:Null}">
<Border BorderThickness="3" Name="border1" CornerRadius="15" BorderBrush="#FF070707" Margin="0,0,0,0">
<Border BorderBrush="Silver" BorderThickness="3" Name="border2" CornerRadius="15" Background="#FF413E3E">
<TextBlock Name="textBlock1" TextAlignment="Center" HorizontalAlignment="Center" VerticalAlignment="Center" Margin="4" Foreground="White" />
</Border>
</Border>
</Grid>
</ContentControl>
The cs file looks like this:
public partial class CtrpushPinContent : ContentControl
{
public static readonly DependencyProperty CaptionProperty =
DependencyProperty.Register("Text",
typeof(string),
typeof(CtrpushPinContent),
new PropertyMetadata(string.Empty));
public string Text
{
get { return textBlock1.Text; }
set { textBlock1.Text = value; }
}
public CtrpushPinContent()
{
InitializeComponent();
}
}
On the main PhoneApplicationPage I try to do the following:
<phone:PhoneApplicationPage.Resources>
<Style TargetType="my:Pushpin" x:Key="PushpinStyle">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="my:Pushpin">
<Grid x:Name="ContentGrid">
<StackPanel Orientation="Vertical">
<Grid Background="{x:Null}" HorizontalAlignment="Right" MinHeight="31" MinWidth="29">
<LJTileSources:CtrpushPinContent HorizontalAlignment="Right" Text="{TemplateBinding Content}" Margin="4" ContentTemplate="{TemplateBinding ContentTemplate}" />
</Grid>
<Image Source="/WifiHotSpot;component/Images/blackPinNoShadow.png" Width="54" Height="54" HorizontalAlignment="Center"></Image>
</StackPanel>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</phone:PhoneApplicationPage.Resources>
<Grid>
<my:Map Margin="0,1,0,0" Name="map1" LogoVisibility="Collapsed" Height="576" CredentialsProvider="key" ZoomLevel="2">
<my:Pushpin Style="{StaticResource PushpinStyle}" Content="Test" Location="50.0863762,14.42814" PositionOrigin="BottomLeft"></my:Pushpin>
</my:Map>
</Grid>
However my solution is not working. I cannot see any effect of the
<my:Pushpin Style="{StaticResource PushpinStyle}" Content="Test" .../>
I believe the problem is somewhere in the style declaration:
<LJTileSources:CtrpushPinContent HorizontalAlignment="Right" Text="{TemplateBinding Content}" Margin="4" ContentTemplate="{TemplateBinding ContentTemplate}" />
because when I change it to
<LJTileSources:CtrpushPinContent HorizontalAlignment="Right" Text="TestText" Margin="4" ContentTemplate="{TemplateBinding ContentTemplate}" />
it displays the "TestText" as required.

In your case for solve this problem quickly you need to implement this:
public static readonly DependencyProperty CaptionProperty =
DependencyProperty.Register("Text",
typeof(string),
typeof(CtrpushPinContent),
new PropertyMetadata(string.Empty, OnTextChanged));
public string Text
{
get { return textBlock1.Text; }
set { textBlock1.Text = value; }
}
private static void OnTextChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
{
((CtrpushPinContent)sender).textBlock1.Text = (string)e.NewValue;
}
public CtrpushPinContent()
{
InitializeComponent();
}
When you set text in Pushpin template:
<LJTileSources:CtrpushPinContent HorizontalAlignment="Right" Text="{TemplateBinding Content}"
Margin="4" ContentTemplate="{TemplateBinding ContentTemplate}" />
you don't set Text property in your control, you set value of dependency property CaptionProperty, because you registered it with name "Text". So when you set Text from xaml, you set exactly Caption property, not Text property of your control. So you need to create event of changing this dependency property for updating text in textBox1.

Related

wpf xaml MVVM inheritance with multiple ContentPresenter

I am rewriting import masks that have a lot in common, so I want (and must) use inheritance.
I have a basic UserControl with all common controls: (I have left out the grid definitions)
BaseClass.xaml
<UserControl x:Class="BaseImport.BaseClass"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<UserControl.Template>
<ControlTemplate TargetType="UserControl">
<Grid>
<Border Grid.Row="0" Grid.Column="0">
<StackPanel>
<Label Content="Text1:"/>
<ComboBox Name="cbText1" MinWidth="80"/>
</StackPanel>
</Border>
<Border Grid.Row="0" Grid.Column="1">
<StackPanel>
<Label Content="Text2:"/>
<ComboBox Name="cbText2" MinWidth="80"/>
</StackPanel>
</Border>
<Border Grid.Row="0" Grid.Column="2">
<StackPanel>
<ContentPresenter ContentSource="Content"/> <!-- ContentSource="Content" is the default-->
</StackPanel>
</Border>
<!-- next Row -->
<Border Grid.Row="1" Grid.Column="0">
<StackPanel>
<Label Content="Text3:"/>
<TextBox Name="tbText3" TextWrapping="Wrap" Text="" MinWidth="80" VerticalAlignment="Center"/>
</StackPanel>
</Border>
<Border Grid.Row="1" Grid.Column="1">
<StackPanel>
<ContentPresenter/>
</StackPanel>
</Border>
</Grid>
</ControlTemplate>
</UserControl.Template>
</UserControl>
This is a kind of Template that gets "used" like this:
MainWindow.xaml (just for demonstration a mainwindow)
<Window x:Class="zzz.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:my="clr-namespace:BaseImport;assembly=BaseImport"
mc:Ignorable="d"
Title="MainWindow" Height="280" Width="600">
<my:BaseClass>
<StackPanel>
<Label Content="Test:"/>
<ComboBox ItemsSource="{Binding TestTyps}" MinWidth="80"/>
</StackPanel>
</my:BaseClass>
</Window>
MainWindow.xaml.cs
using WpfApp1.ViewModel;
namespace zzz
{
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
this.DataContext = new MainViewModel();
}
}
}
and to wrap it up MainViewModel.cs:
namespace WpfApp1.ViewModel
{
public class MainViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler? PropertyChanged;
public string[] TestTyps { get { return new string[] { "abc", "123", "xyz" }; } }
}
}
If I have one ContentPresenter everything works fine. But in the BaseClass I have two, potentially more.
Like this only the "last" Presenter gets populated. And in MainWindow.xaml can only be one declared.
How can I put more Content in MainWindow.xaml?
How can I select the right one?
Thanks
The red rectangle is were the second presenter is located (row 1, column 1) but I want it to be were the arrow points (row 0, column 2).
I want another control in place of the red rectangle also declared in MainWindow.xaml.
The stuff you put directly in the <my:BaseClass> tags is the contentProperty which can be only one.
Why only the last ContentPresenter shows it? Because each VisualElement can only have one parent, so the last one claiming it wins.
However you can create as many properties as you want.
<UserControl x:Class="BaseImport.BaseClass"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<UserControl.Template>
<ControlTemplate TargetType="UserControl">
<Grid>
<Border Grid.Row="0" Grid.Column="0">
<StackPanel>
<Label Content="Text1:"/>
<ComboBox Name="cbText1" MinWidth="80"/>
</StackPanel>
</Border>
<Border Grid.Row="0" Grid.Column="1">
<StackPanel>
<Label Content="Text2:"/>
<ComboBox Name="cbText2" MinWidth="80"/>
</StackPanel>
</Border>
<Border Grid.Row="0" Grid.Column="2">
<StackPanel>
<ContentPresenter ContentSource="FirstContent"/>
</StackPanel>
</Border>
<!-- next Row -->
<Border Grid.Row="1" Grid.Column="0">
<StackPanel>
<Label Content="Text3:"/>
<TextBox Name="tbText3" TextWrapping="Wrap" Text="" MinWidth="80" VerticalAlignment="Center"/>
</StackPanel>
</Border>
<Border Grid.Row="1" Grid.Column="1">
<StackPanel>
<ContentPresenter ContentSource="SecondContent"/>
</StackPanel>
</Border>
</Grid>
</ControlTemplate>
</UserControl.Template>
</UserControl>
<my:BaseClass>
<my:BaseClass.FirstContent>
<StackPanel>
<Label Content="Test:"/>
<ComboBox ItemsSource="{Binding TestTyps}" MinWidth="80"/>
</StackPanel>
<my:BaseClass.FirstContent>
<my:BaseClass.SecondContent>
<StackPanel>
<Label Content="Test10:"/>
<TextBox Text="{Binding Whatever}" MinWidth="80"/>
</StackPanel>
<my:BaseClass.SecondContent>
</my:BaseClass>
I gave the point to Firo, because he gave me the right answer. But I want to give some final thoughts, so everyone can benefit from it.
For the xaml "base-class":
<UserControl
x:Class="BaseImport.BaseClass"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<UserControl.Template>
<ControlTemplate TargetType="UserControl">
<Grid>
<Border Grid.Row="0" Grid.Column="0">
<StackPanel>
<Label Content="Text1:"/>
<ComboBox Name="cbText1" MinWidth="80"/>
</StackPanel>
</Border>
<Border Grid.Row="0" Grid.Column="1">
<StackPanel>
<Label Content="DB:"/>
<ComboBox
Name="cbxDB"
ItemsSource="{Binding DBs}"
SelectedItem="{Binding DB}"
MinWidth="80"
SelectionChanged="selectionChanged"
Loaded="DB_Loaded">
<!--
here the DataContext can be set differently
<ComboBox.DataContext>
<Views:DBViewModel/>
</ComboBox.DataContext>
-->
</ComboBox>
</StackPanel>
</Border>
<!-- the content placeholder -->
<Border Grid.Row="0" Grid.Column="2">
<StackPanel MinWidth="80">
<ContentControl
Content="{Binding ContentOne}"
ContentTemplate="{Binding ContentOneTemplate}"
ContentTemplateSelector="{Binding ContentOneTemplateSelector}"/>
</StackPanel>
</Border>
<Border Grid.Row="0" Grid.Column="3">
<StackPanel MinWidth="80">
<ContentControl
Content="{Binding ContentTwo}"
ContentTemplate="{Binding ContentTwoTemplate}"
ContentTemplateSelector="{Binding ContentTwoTemplateSelector}"/>
</StackPanel>
</Border>
</Grid>
</ControlTemplate>
</UserControl.Template>
</UserControl>
For the "base-class":
public partial class BaseClass : UserControl
{
public BaseClass(BaseViewModel _bvm)
{
InitializeComponent();
// set DataContext for all other controls
DataContext = _bvm;
}
#region DependencyProperty
public static readonly DependencyProperty ContentOneProperty = DependencyProperty.Register("ContentOne", typeof(object), typeof(BaseImportClass));
public static readonly DependencyProperty ContentOneTemplateProperty = DependencyProperty.Register("ContentOneTemplate", typeof(DataTemplate), typeof(BaseImportClass));
public static readonly DependencyProperty ContentOneTemplateSelectorProperty = DependencyProperty.Register("ContentOneTemplateSelector", typeof(DataTemplateSelector), typeof(BaseImportClass));
public static readonly DependencyProperty ContentTwoProperty = DependencyProperty.Register("ContentTwo", typeof(object), typeof(BaseImportClass));
public static readonly DependencyProperty ContentTwoTemplateProperty = DependencyProperty.Register("ContentTwoTemplate", typeof(DataTemplate), typeof(BaseImportClass));
public static readonly DependencyProperty ContentTwoTemplateSelectorProperty = DependencyProperty.Register("ContentTwoTemplateSelector", typeof(DataTemplateSelector), typeof(BaseImportClass));
public object ContentOne
{
get { return GetValue(ContentOneProperty); }
set { SetValue(ContentOneProperty, value); }
}
public DataTemplate ContentOneTemplate
{
get { return (DataTemplate)GetValue(ContentOneTemplateProperty); }
set { SetValue(ContentOneTemplateProperty, value); }
}
public DataTemplateSelector ContentOneTemplateSelector
{
get { return (DataTemplateSelector)GetValue(ContentOneTemplateSelectorProperty); }
set { SetValue(ContentOneTemplateSelectorProperty, value); }
}
public object ContentTwo
{
get { return GetValue(ContentTwoProperty); }
set { SetValue(ContentTwoProperty, value); }
}
public DataTemplate ContentTwoTemplate
{
get { return (DataTemplate)GetValue(ContentTwoTemplateProperty); }
set { SetValue(ContentTwoTemplateProperty, value); }
}
public DataTemplateSelector ContentTwoTemplateSelector
{
get { return (DataTemplateSelector)GetValue(ContentTwoTemplateSelectorProperty); }
set { SetValue(ContentTwoTemplateSelectorProperty, value); }
}
#endregion
}
For the "derived" class xaml:
<UserControl x:Class="WpfApp1.DerivedClass"
xmlns:base="clr-namespace:BaseImport.Views;assembly=BaseImport"
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">
<StackPanel>
<base:BaseClass Name="bc">
<base:BaseClass.ContentOneTemplate>
<DataTemplate>
<StackPanel>
<Label Content="Test:"/>
<ComboBox
ItemsSource="{Binding TestTyps}"
SelectedItem="{Binding TestTyp}"
SelectionChanged="TestTypChanged"
Loaded="TestTypLoaded">
<!--
here the DataContext can be set differently
<ComboBox.DataContext>
<Views:ViewModelXYZ/>
</ComboBox.DataContext>
-->
</ComboBox>
</StackPanel>
</DataTemplate>
</base:BaseClass.ContentOneTemplate>
</base:BaseClass>
</StackPanel>
</UserControl>
For the "derived" class:
public partial class DerivedClass : UserControl
{
ViewModelABC vmabc = null;
public DerivedClass(ViewModel _vm) : this()
{
vmabc = _vm;
this.DataContext = vmabc;
bc.DataContext = vmabc; // or another viewmodel that holds TestTyps and TestTyp or leave it for ViewModelXYZ
}
private void TestTypChanged(object sender, SelectionChangedEventArgs e)
{
PropertyChanged();
}
private void TestTypLoaded(object sender, RoutedEventArgs e)
{
(sender as ComboBox).SelectedValue = (this.DataContext as ViewModel(XYZ/ABC).TestTyp;
}
}
That is what I came up.
I hope that helps others.

MVVM WPF Add new item to the DataGrid from Textboxes which are bound to the nested properties of the Model class

I am new to WPF and also the MVVM pattern, I tried to create a simple WPF project to query a database of I-Shape Steel Profiles and show it to the user through the DataGrid. After I have successfully done it, I am now moving to the next part: Letting the user to add new steel profiles to the DataGrid by filling out a set of TextBoxes.
Model
namespace SectionGuardWPF.MVVM.Model
{
public class IProfileModel : ObservableObject
{
#region Private Fields
public short _recno;
public string _name;
public string _staadName;
public double _ax;
public double _d;
public double _bf;
public double _tf;
public double _tw;
public double _iz;
public double _ix;
public double _ct;
public double _iy;
public double _zx;
public double _zy;
#endregion
#region Public Properties
/// <summary>
/// Properties are given NotifyPropertyChanged method to allow for a change in property to be notified
/// </summary>
public short RECNO
{
get { return _recno; }
set
{
_recno = value;
OnPropertyChanged();
}
}
public string Name
{
get { return _name; }
set
{
_name = value;
OnPropertyChanged();
}
}
...
// Repeated for the other fields
ObservableObject: Base class that implements INotifyPropertyChanged
namespace SectionGuardWPF.Core
{
public class ObservableObject : INotifyPropertyChanged
{
/// <summary>
/// Raised when a property on this object has a new value.
/// </summary>
public event PropertyChangedEventHandler PropertyChanged;
/// <summary>
/// Raises this object's PropertyChanged event.
/// </summary>
protected void OnPropertyChanged([CallerMemberName] string name = null) // If you use the CallerMemberName attribute, calls to the NotifyPropertyChanged method don't have to specify the property name as a string argument
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
}
}
}
RelayCommand
namespace SectionGuardWPF.Core
{
class RelayCommand : ICommand
{
private Action<object> _execute;
private Func<object, bool> _canExecute;
public event EventHandler CanExecuteChanged
{
add { CommandManager.RequerySuggested += value; }
remove { CommandManager.RequerySuggested -= value; }
}
public RelayCommand(Action<object> execute, Func<object,bool> canExecute=null)
{
_execute = execute;
_canExecute = canExecute;
}
public bool CanExecute(object parameter)
{
return _canExecute == null || _canExecute(parameter);
}
public void Execute(object parameter)
{
_execute(parameter);
}
}
}
View Model
namespace SectionGuardWPF.MVVM.ViewModel
{
public class IProfilesViewModel:ObservableObject
{
#region Private Fields
private readonly string _connectionString = //Hidden
private ObservableCollection<IProfileModel> _iProfiles;
private ICommand _getIProfilesCommand;
private ICommand _addNewIProfileCommand;
private IProfileModel _iProfile = new IProfileModel();
#endregion
#region Constructor
public IProfilesViewModel()
{
// Set default Profile Database for the DataGrid upon View instantiation
IProfiles = GetIProfiles();
}
#endregion
#region Public Properties and Commands
/// <summary>
/// Public properties of the IProfilesViewModel Class (used for view binding with IProfilesSubView)
/// </summary>
public ObservableCollection<IProfileModel> IProfiles
{
get { return _iProfiles; }
set
{
_iProfiles = value;
OnPropertyChanged();
}
}
public IProfileModel IProfile
{
get { return _iProfile; }
set
{
_iProfile = value;
OnPropertyChanged();
}
}
public ICommand GetIProfilesCommand
{
get
{
if (_getIProfilesCommand == null)
{
_getIProfilesCommand = new RelayCommand(
param => GetIProfiles()
);
}
return _getIProfilesCommand;
}
}
public ICommand AddNewIProfileCommand
{
get
{
if (_addNewIProfileCommand == null)
{
_addNewIProfileCommand = new RelayCommand(
param => AddNewIProfile()
);
}
return _addNewIProfileCommand;
}
}
#endregion
#region Private Methods
private ObservableCollection<IProfileModel> GetIProfiles()
{
TableDataProvider tableDataProvider = new TableDataProvider(_connectionString);
tableDataProvider.QueryString = "SELECT * FROM [I Shape]";
IEnumerable<IProfileModel> iProfiles = tableDataProvider.Query<IProfileModel>();
ObservableCollection<IProfileModel> iProfileObsvCol = new ObservableCollection<IProfileModel>(iProfiles);
return iProfileObsvCol;
}
private void AddNewIProfile()
{
IProfiles.Add(
new IProfileModel
{
RECNO = IProfile.RECNO,
Name = IProfile.Name,
StaadName = IProfile.StaadName,
AX = IProfile.AX,
D = IProfile.D,
Bf = IProfile.Bf,
Tf = IProfile.Tf,
Tw = IProfile.Tw,
Ix = IProfile.Ix,
Iy = IProfile.Iy,
Iz = IProfile.Iz,
Ct = IProfile.Ct,
Zx = IProfile.Zx,
Zy = IProfile.Zy
}
);
}
#endregion
}
}
View
<UserControl x:Class="SectionGuardWPF.MVVM.View.IProfilesSubView"
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:SectionGuardWPF.MVVM.View"
xmlns:viewModel= "clr-namespace:SectionGuardWPF.MVVM.ViewModel"
xmlns:view="clr-namespace:SectionGuardWPF.MVVM.View"
mc:Ignorable="d"
d:DesignHeight="580" d:DesignWidth="1300">
<UserControl.DataContext>
<viewModel:IProfilesViewModel/>
</UserControl.DataContext>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="620"/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<DataGrid x:Name="ProfilesDataGrid"
x:FieldModifier="public"
ItemsSource="{Binding IProfiles, Mode=TwoWay}">
</DataGrid>
<Border Grid.Column="1"
CornerRadius="20"
Background ="White"
Margin="10">
<Grid Margin="10">
<Grid.RowDefinitions>
<RowDefinition Height="50"/>
<RowDefinition/>
<RowDefinition Height="150"/>
</Grid.RowDefinitions>
<TextBlock Text="I-Profiles Data Viewer"
VerticalAlignment="Center"
HorizontalAlignment="Left"
Margin="20,0,0,0"
Foreground="Black"
FontSize="22"/>
<Grid Grid.Row="1">
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition/>
<RowDefinition/>
<RowDefinition/>
<RowDefinition/>
<RowDefinition/>
<RowDefinition/>
</Grid.RowDefinitions>
<TextBlock Text="RECNO"
VerticalAlignment="Center"
HorizontalAlignment="Left"
Margin="20,0,0,0"
Foreground="#1A80FA"
Grid.Column="0"
Grid.Row="0"
FontSize="14"/>
<TextBox x:Name="RECNOTextBox"
Margin="0,0,20,0"
VerticalAlignment="Center"
HorizontalAlignment="Right"
Style="{StaticResource ParameterTextbox}"
Grid.Column="0"
Grid.Row="0"
Text="{Binding IProfile.RECNO, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}"/>
<TextBlock Text="Name"
VerticalAlignment="Center"
HorizontalAlignment="Left"
Margin="20,0,0,0"
Foreground="#1A80FA"
Grid.Column="0"
Grid.Row="1"
FontSize="14"/>
<TextBox x:Name="NameTextBox"
Margin="0,0,20,0"
VerticalAlignment="Center"
HorizontalAlignment="Right"
Style="{StaticResource ParameterTextbox}"
Grid.Column="0"
Grid.Row="1"
Text="{Binding IProfile.Name, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}"/>
<TextBlock Text="StaadName"
VerticalAlignment="Center"
HorizontalAlignment="Left"
Margin="20,0,0,0"
Foreground="#1A80FA"
Grid.Column="0"
Grid.Row="2"
FontSize="14"/>
<TextBox x:Name="StaadNameTextBox"
Margin="0,0,20,0"
VerticalAlignment="Center"
HorizontalAlignment="Right"
Style="{StaticResource ParameterTextbox}"
Grid.Column="0"
Grid.Row="2"
Text="{Binding IProfile.StaadName, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}"/>
<TextBlock Text="AX"
VerticalAlignment="Center"
HorizontalAlignment="Left"
Margin="20,0,0,0"
Foreground="#1A80FA"
Grid.Column="0"
Grid.Row="3"
FontSize="18"/>
<TextBox x:Name="AXTextBox"
Margin="0,0,20,0"
VerticalAlignment="Center"
HorizontalAlignment="Right"
Style="{StaticResource ParameterTextbox}"
Grid.Column="0"
Grid.Row="3"
Text="{Binding IProfile.AX, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}"/>
<TextBlock Text="D"
VerticalAlignment="Center"
HorizontalAlignment="Left"
Margin="20,0,0,0"
Foreground="#1A80FA"
Grid.Column="0"
Grid.Row="4"
FontSize="18"/>
<TextBox x:Name="DTextBox"
Margin="0,0,20,0"
VerticalAlignment="Center"
HorizontalAlignment="Right"
Style="{StaticResource ParameterTextbox}"
Grid.Column="0"
Grid.Row="4"
Text="{Binding IProfile.D, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}"/>
<TextBlock Text="Bf"
VerticalAlignment="Center"
HorizontalAlignment="Left"
Margin="20,0,0,0"
Foreground="#1A80FA"
Grid.Column="0"
Grid.Row="5"
FontSize="18"/>
<TextBox x:Name="BfTextBox"
Margin="0,0,20,0"
VerticalAlignment="Center"
HorizontalAlignment="Right"
Style="{StaticResource ParameterTextbox}"
Grid.Column="0"
Grid.Row="5"
Text="{Binding IProfile.Bf, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}"/>
<TextBlock Text="Tf"
VerticalAlignment="Center"
HorizontalAlignment="Left"
Margin="20,0,0,0"
Foreground="#1A80FA"
Grid.Column="0"
Grid.Row="6"
FontSize="18"/>
<TextBox x:Name="TfTextBox"
Margin="0,0,20,0"
VerticalAlignment="Center"
HorizontalAlignment="Right"
Style="{StaticResource ParameterTextbox}"
Grid.Column="0"
Grid.Row="6"
Text="{Binding IProfile.Tf, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}"/>
<TextBlock Text="Tw"
VerticalAlignment="Center"
HorizontalAlignment="Left"
Margin="20,0,0,0"
Foreground="#1A80FA"
Grid.Column="1"
Grid.Row="0"
FontSize="18"/>
<TextBox x:Name="TwTextBox"
Margin="0,0,20,0"
VerticalAlignment="Center"
HorizontalAlignment="Right"
Style="{StaticResource ParameterTextbox}"
Grid.Column="1"
Grid.Row="0"
Text="{Binding IProfile.Tw, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}"/>
<TextBlock Text="Ix"
VerticalAlignment="Center"
HorizontalAlignment="Left"
Margin="20,0,0,0"
Foreground="#1A80FA"
Grid.Column="1"
Grid.Row="1"
FontSize="18"/>
<TextBox x:Name="IxTextBox"
Margin="0,0,20,0"
VerticalAlignment="Center"
HorizontalAlignment="Right"
Style="{StaticResource ParameterTextbox}"
Grid.Column="1"
Grid.Row="1"
Text="{Binding IProfile.Ix, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}"/>
<TextBlock Text="Iy"
VerticalAlignment="Center"
HorizontalAlignment="Left"
Margin="20,0,0,0"
Foreground="#1A80FA"
Grid.Column="1"
Grid.Row="2"
FontSize="18"/>
<TextBox x:Name="IyTextBox"
Margin="0,0,20,0"
VerticalAlignment="Center"
HorizontalAlignment="Right"
Style="{StaticResource ParameterTextbox}"
Grid.Column="1"
Grid.Row="2"
Text="{Binding IProfile.Iy, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}"/>
<TextBlock Text="Iz"
VerticalAlignment="Center"
HorizontalAlignment="Left"
Margin="20,0,0,0"
Foreground="#1A80FA"
Grid.Column="1"
Grid.Row="3"
FontSize="18"/>
<TextBox x:Name="IzTextBox"
Margin="0,0,20,0"
VerticalAlignment="Center"
HorizontalAlignment="Right"
Style="{StaticResource ParameterTextbox}"
Grid.Column="1"
Grid.Row="3"
Text="{Binding IProfile.Iz, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}"/>
<TextBlock Text="Ct"
VerticalAlignment="Center"
HorizontalAlignment="Left"
Margin="20,0,0,0"
Foreground="#1A80FA"
Grid.Column="1"
Grid.Row="4"
FontSize="18"/>
<TextBox x:Name="CtTextBox"
Margin="0,0,20,0"
VerticalAlignment="Center"
HorizontalAlignment="Right"
Style="{StaticResource ParameterTextbox}"
Grid.Column="1"
Grid.Row="4"
Text="{Binding IProfile.Ct, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}"/>
<TextBlock Text="Zx"
VerticalAlignment="Center"
HorizontalAlignment="Left"
Margin="20,0,0,0"
Foreground="#1A80FA"
Grid.Column="1"
Grid.Row="5"
FontSize="18"/>
<TextBox x:Name="ZxTextBox"
Margin="0,0,20,0"
VerticalAlignment="Center"
HorizontalAlignment="Right"
Style="{StaticResource ParameterTextbox}"
Grid.Column="1"
Grid.Row="5"
Text="{Binding IProfile.Zx, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}"/>
<TextBlock Text="Zy"
VerticalAlignment="Center"
HorizontalAlignment="Left"
Margin="20,0,0,0"
Foreground="#1A80FA"
Grid.Column="1"
Grid.Row="6"
FontSize="18"/>
<TextBox x:Name="ZyTextBox"
Margin="0,0,20,0"
VerticalAlignment="Center"
HorizontalAlignment="Right"
Style="{StaticResource ParameterTextbox}"
Grid.Column="1"
Grid.Row="6"
Text="{Binding IProfile.Zy, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}"/>
</Grid>
<StackPanel Grid.Row="2"
Orientation="Horizontal">
<Button x:Name="AddButton"
Style="{StaticResource AddButton}"
Content="Add"
Background="#28C941"
Height="50"
Width="150"
Margin="85,0,0,0"
Command="{Binding AddNewIProfileCommand}"/>
<Button x:Name="EditButton"
Style="{StaticResource AddButton}"
Content="Edit"
Background="#FFBD2E"
Height="50"
Width="150"
Margin="20,0,0,0"/>
<Button x:Name="DeleteButton"
Style="{StaticResource AddButton}"
Content="Delete"
Background="#FF6059"
Height="50"
Width="150"
Margin="20,0,0,0"/>
</StackPanel>
</Grid>
</Border>
</Grid>
</UserControl>
My Model class is a class called the IProfileModel that represents the I-Shape Profiles, I have made so that it inherits from the ObservableObject class so that it can use the NotifyPropertyChanged methods and I have made so that the properties of the Model class has implemented the OnPropertyChanged() Method.
In my View, I have bound the source of the DataGrid to the IProfiles which is an ObservableCollection that stores instances of the IProfileModel. Each TextBox corresponds to one property of the IProfileModel class, thus the Text property of each TextBox is bound to its respective nested property of the IProfile property of the ViewModel. The "Add" Button is bound to the respective ICommand in the ViewModel called the AddNewIProfileCommand
In my ViewModel, I have added the IProfileModel as one of its properties using the name IProfile, the TextBox.Text properties are bound to the nested properties of the IProfile as shown in the XAML of the View. As for the AddNewIProfileCommand, I have made so that it corresponds to a private method called the AddNewIProfile() which will add a new IProfileModel object whose properties will refer to the nested properties of the IProfile property that is bound to the TextBoxes.
Edit:
I want to add that the AddNewIProfile() will add the new IProfileModel object to the IProfiles which is bound to the DataGrid
My main issue is that whenever I modified the TextBoxes and then clicked on the Button to add the profiles to the DataGrid, the newly added entry to the DataGrid is always a IProfileModel with empty property values (all string properties of the IProfileModel are null and all numerical type properties are 0). It seems like modifying the TextBoxes does not change the nested properties of the IProfile even though I have set the UpdateSourceTrigger to PropertyChanged and the Mode to Two Way and the IProfile itself is of the IProfileModel class which has inherited from the ObservableObject class.
Lastly, I know that my question is quite similar to this one but I have followed the suggestions there and tried to match their solution but I somehow can't get it to work.
Thanks!
Edit:
Added some before - after picture of the DataGrid after the "Add" Button is pressed:
Edit:
The ParameterTextbox Style which was the cause of the error as pointed out by Cédric Moers in the comments:
<Style TargetType="{x:Type TextBox}"
x:Key="ParameterTextbox">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type TextBox}">
<Border CornerRadius="5"
Background="White"
Width="180"
Height="35"
BorderThickness="3"
BorderBrush="#EAEAEA">
<Grid>
<Rectangle StrokeThickness="1"/>
<TextBox Margin="1"
Text="{TemplateBinding Text}"
BorderThickness="0"
Background="Transparent"
VerticalContentAlignment="Center"
Padding="5"
Foreground="Black"
x:Name="SearchBox"/>
</Grid>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
I tried to replicate your problem but I had to remove the styles since you did not provide them. It works at my side, so perhaps the problem lies within the style?
Please delete your styles for the textboxes (ParameterTextbox) and see if it works then. If you can provide me with the source code for the style, I can take a look at it. Maybe you are overriding the text property.
edit after it turned out the style was the issue
here is an example of the style you want to accomplish in a minimalistic way.
<UserControl.Resources>
<ControlTemplate x:Key="RoundedTextBoxCt">
<Border
Width="{TemplateBinding Width}"
Height="{TemplateBinding Height}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
Background="{TemplateBinding Background}"
x:Name="Bd"
CornerRadius="5">
<ScrollViewer
Padding="{TemplateBinding Padding}"
VerticalContentAlignment="{TemplateBinding VerticalContentAlignment}"
Foreground="{TemplateBinding Foreground}"
x:Name="PART_ContentHost"/>
</Border>
</ControlTemplate>
<Style x:Key="ParameterTextBox" TargetType="TextBox">
<Setter Property="Width" Value="180"/>
<Setter Property="Height" Value="35"/>
<Setter Property="VerticalContentAlignment" Value="Center"/>
<Setter Property="Padding" Value="5"/>
<Setter Property="Foreground" Value="Black"/>
<Setter Property="BorderBrush" Value="#EAEAEA"/>
<Setter Property="BorderThickness" Value="3"/>
<Setter Property="Background" Value="White"/>
<Setter Property="Template" Value="{StaticResource RoundedTextBoxCt}"/>
</Style>
...
</UserControl.Resources>
I made the control template as a separate resource, this way 'rounded textbox' can be put inside of some 'shared' assembly, and you will be able to use it for other projects as well.
It is minimalistic, as in the minimal you'll need. If you want a full-fledged text box, copy the style from here and modify it: https://learn.microsoft.com/en-us/dotnet/desktop/wpf/controls/textbox-styles-and-templates?view=netframeworkdesktop-4.8
it might look scary, but most of the time it comes down to modifying only a few rules. Once you get the hang of it, it's quite easy.
Instead of working with the setters, and even a separate control template resource, you could hard-code everything within the control template, WITHIN the setter of the control template of the textbox style, but that's up to you.
Good luck!

how to Detect selected item in ContentControl in wpf

I'm trying to develop a HamburgerMenu. a ContentControl that i restyled it.
my xaml code is something like this :
<Style TargetType="local:HamburgerMenu">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="local:HamburgerMenu">
<Grid x:Name="mainGrid" Background="{TemplateBinding Background}">
<!--HamburgerMenu button-->
<Border x:Name="border" Background="{TemplateBinding Background}" HorizontalAlignment="Left" VerticalAlignment="Top" Height="40" Width="50">
<ToggleButton x:Name="menuIcon" Background="Red" HorizontalAlignment="Left" VerticalAlignment="Top" Height="40" Width="50" IsChecked="{Binding RelativeSource={RelativeSource AncestorType={x:Type local:HamburgerMenu}}, Path=IsOpen}">
<Path x:Name="path" HorizontalAlignment="Center" VerticalAlignment="Center" Stretch="Uniform" Width="30" Fill="{TemplateBinding MenuIconColor}" Data="M2,15.5L22,15.5 22,17.5 2,17.5 2,15.5z M2,10.5L22,10.5 22,12.5 2,12.5 2,10.5z M2,5.5L22,5.5 22,7.5 2,7.5 2,5.5z"/>
</ToggleButton>
</Border>
<!-- HamburgerMenu Items List-->
<ListBox x:Name="listbox" ItemsSource="{TemplateBinding Content}" HorizontalAlignment="Left" Margin="0,40,0,0" VerticalAlignment="Top" ScrollViewer.HorizontalScrollBarVisibility="Disabled" SelectedIndex="0"/>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Style TargetType="local:HamburgerMenuItem">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="local:HamburgerMenuItem">
<Button x:Name="ListBoxItemButton" Command="{TemplateBinding SelectionCommand}" HorizontalContentAlignment="Stretch" VerticalContentAlignment="Stretch">
<Grid x:Name="MainGrid">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="5" />
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<Grid Grid.ColumnSpan="2">
<Grid Background="Transparent" Margin="0" Width="300">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="45" />
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<Grid Grid.Column="1">
<TextBlock Text="{TemplateBinding Text}" Margin="10,0,0,0" HorizontalAlignment="Left" VerticalAlignment="Center" FontFamily="Segoe UI Light" FontSize="18" Foreground="{TemplateBinding Foreground}" TextWrapping="Wrap"/>
</Grid>
<Grid Grid.Column="0">
<Image Source="{TemplateBinding Icon}" Margin="10,5,5,5"/>
</Grid>
</Grid>
</Grid>
<Grid Name="ItemSelectedIndicator" Grid.Column="0" Background="{TemplateBinding SelectionIndicatorColor}" Visibility="Collapsed" />
</Grid>
</Button>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
and this is my HamburgerMenu classes :
public class HamburgerMenu : ContentControl
{
public new List<HamburgerMenuItem> Content
{
get { return (List<HamburgerMenuItem>)GetValue(ContentProperty); }
set { SetValue(ContentProperty, value); }
}
public new static readonly DependencyProperty ContentProperty =
DependencyProperty.Register("Content", typeof(List<HamburgerMenuItem>), typeof(HamburgerMenu),
new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.AffectsRender | FrameworkPropertyMetadataOptions.AffectsMeasure));
static HamburgerMenu()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(HamburgerMenu), new FrameworkPropertyMetadata(typeof(HamburgerMenu)));
}
public override void BeginInit()
{
Content = new List<HamburgerMenuItem>();
base.BeginInit();
}
}
public class HamburgerMenuItem : ListBoxItem
{
static HamburgerMenuItem()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(HamburgerMenuItem), new FrameworkPropertyMetadata(typeof(HamburgerMenuItem)));
}
public string Text
{
get { return (string)GetValue(TextProperty); }
set { SetValue(TextProperty, value); }
}
public static readonly DependencyProperty TextProperty =
DependencyProperty.Register("Text", typeof(string), typeof(HamburgerMenuItem), new PropertyMetadata(String.Empty));
}
my question is how i can find witch ListBox item is selected in my window where i used my custom Control.
You could add a "SelectedItem" dependency property to your custom control and bind the SelectedItem property of the ListBox in the ControlTemplate to this one:
<!-- HamburgerMenu Items List-->
<ListBox x:Name="listbox" ItemsSource="{TemplateBinding Content}"
SelectedItem="{Binding SelectedItem, RelativeSource={RelativeSource TemplatedParent}}"
HorizontalAlignment="Left" Margin="0,40,0,0" VerticalAlignment="Top" ScrollViewer.HorizontalScrollBarVisibility="Disabled" SelectedIndex="0"/>
You then bind your new dependency property to the source property in your window:
<local:HamburgerMenuItem ... SelectedItem="{Binding YourSourceProperty}" />

XAML binding value

I am developing windows phone 8.1 app and I need circular progressbar.
I have this usercontrol code:
<UserControl.Resources>
<Style TargetType="ProgressBar" x:Key="CircularProgressBarStyle">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ProgressBar">
<Grid x:Name="LayoutRoot">
<local:CircularProgressBarViewModel.Attach>
<local:CircularProgressBarViewModel HoleSizeFactor="0.75"/>
</local:CircularProgressBarViewModel.Attach>
<Ellipse Width="{Binding Diameter}" Height="{Binding Diameter}"
HorizontalAlignment="Center" VerticalAlignment="Center"
Stroke="LightGray" Opacity="0.5" Fill="Transparent"
StrokeThickness="10">
</Ellipse>
<local:PiePiece CentreX="{Binding CentreX}" CentreY="{Binding CentreY}"
RotationAngle="0" WedgeAngle="{Binding Angle}"
Radius="{Binding Radius}" InnerRadius="{Binding InnerRadius}"
Fill="Black" Opacity="0.7"/>
<Grid util:GridUtils.RowDefinitions="*,2*,*"
util:GridUtils.ColumnDefinitions="*,2*,*">
<Viewbox Grid.Row="1" Grid.Column="1">
<TextBlock Name="myValue" Text="{myValue value}"
Foreground="WhiteSmoke"
FontWeight="Bold"
VerticalAlignment="Center" HorizontalAlignment="Center"/>
</Viewbox>
</Grid></Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</UserControl.Resources>
How can I change value with the name "myValue" from code behind (for example, from MainPage.xaml.cs) and not from CircularProgressBarViewModel?
If you need to get myValue from your MainPage, where you are using your UserControl you can create DependencyProperty in your control and set any value from page to control.
public sealed partial class YourUserControl: UserControl
{
public static readonly DependencyProperty myValueProperty = DependencyProperty.Register("myValue", typeof(string), typeof(YourUserControl), new PropertyMetadata(string.Empty));
public YourUserControl()
{
this.InitializeComponent();
}
public string myValue
{
get { return (string)GetValue(myValueProperty ); }
set { SetValue(myValueProperty , value); }
}
}
And on the your MainPage like this:
<controls:YourUserControl myValue={Binding YourValue}/>

Get attached property value from content in contentpresenter

I've made a custom window that has a template defined in a resource dictionary. The window has no title bar.
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:dsControls="clr-namespace:Something">
<Style x:Key="WindowRegionStyle"
TargetType="Window">
/** Other setters **/
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ContentControl">
<Grid>
<Border Background="{StaticResource WindowBackground}"
BorderBrush="{StaticResource BorderBrushColor}"
BorderThickness="1"
CornerRadius="8"
HorizontalAlignment="Center"
VerticalAlignment="Center">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Border CornerRadius="8,8,0,0"
BorderBrush="{StaticResource BorderBrushColor}"
Background="{StaticResource HeaderColor}"
Grid.ColumnSpan="2"
Grid.Row="0">
<Label Content="{Binding Path=(dsControls:WindowDetails.Title), RelativeSource={RelativeSource TemplatedParent}}"
FontSize="20" />
</Border>
<ContentPresenter Grid.Row="1" Grid.ColumnSpan="2" Content="{TemplateBinding Content}" />
</Grid>
</Border>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
The content that is added to this template is a UserControl. That all works.
But now I want to define a title in the UserControl. To set the title, I made an attached property 'WindowDetails.Title'.
public static class WindowDetails
{
public static string GetTitle(DependencyObject obj)
{
return (string)obj.GetValue(TitleProperty);
}
public static void SetTitle(DependencyObject obj, string value)
{
obj.SetValue(TitleProperty, value);
}
// Using a DependencyProperty as the backing store for Title. This enables animation, styling, binding, etc...
public static readonly DependencyProperty TitleProperty =
DependencyProperty.RegisterAttached("Title", typeof(string), typeof(WindowDetails), new PropertyMetadata(""));
}
And I set the the title in the UserControl like this:
<UserControl x:Class="Something.View"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:dsControls="clr-namespace:Something"
Width="400"
dsControls:WindowDetails.Title="Test">
/** Some content **/
</UserControl>
The problem
I cannot bind the property value to the label in my template.
What I already tried (and went wrong)
<Label Content="{Binding Path=(dsControls:WindowDetails.Title), RelativeSource={RelativeSource TemplatedParent}}"
FontSize="20" />
and
<Label Content="{Binding Path=(dsControls:WindowDetails.Title), RelativeSource={RelativeSource Self}}"
FontSize="20" />
Also, changing the OwnerType of the Attached property:
To WindowDetails
public static readonly DependencyProperty TitleProperty =
DependencyProperty.RegisterAttached("Title", typeof(string), typeof(WindowDetails), new PropertyMetadata(""));
When I do this, the property is not set. But the content of the label has the default property value.
To UserControl
public static readonly DependencyProperty TitleProperty =
DependencyProperty.RegisterAttached("Title", typeof(string), typeof(UserControl), new PropertyMetadata(""));
When I do this, the property is set, but the content of the label has no value.
Question
How can I set the attached property in my UserControl and bind it to the content of the label in my template?
Thanks in advance!
Greetings
Loetn
This is how you can do this: Give x:Name to your ContentPresenter and refer to it in binding the label.
<Border CornerRadius="8,8,0,0"
BorderBrush="{StaticResource BorderBrushColor}"
Background="{StaticResource HeaderColor}"
Grid.ColumnSpan="2"
Grid.Row="0">
<Label Content="{Binding Path=Content.(dsControls:WindowDetails.Title), ElementName="myPresenter"}" FontSize="20" />
</Border>
<ContentPresenter x:Name="myPresenter" Grid.Row="1" Grid.ColumnSpan="2" Content="{TemplateBinding Content}" />
Try using a converter
public class AttchedTitleToTitleConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
return (value as FrameworkElement).GetValue(WindowDetails.TitleProperty);
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
{
throw new NotSupportedException();
}
}
Add it to your ResourceDictionary:
<local:AttchedTitleToTitleConverter x:Key="titleConverter"/>
And in the binding:
<Label Content="{Binding RelativeSource={RelativeSource Self}, Converter={DynamicResource titleConverter}}"/>
Hope this helps

Categories