I have the following XAML:
<sdk:Label Content="{Binding RefreshTextToggle, Converter={StaticResource enumToText}, ConverterParameter=ItemsOfInterest,FallbackValue='Please select items of interest to you'}"
Style="{StaticResource StandardLabel}"
Height="{Binding ElementName=ItemsOfInterest,Path=Height}"/>
<ListBox Name="ItemsOfInterest"
ItemsSource="{Binding Path=ItemsOfInterest}"
Margin="5"
MinHeight="25"
Width="250"
HorizontalAlignment="Left">
The height of the ItemsOfInterest is dynamic pending on how many elements are in it.
Anyone see what I am doing wrong with the height binding? It isn't even close to the same size as the ItemsOfInterst.
You should bind to ActualHeight, which specifies the height it was arranged at. The Height property allows you to set a fixed height, but doesn't tell you exactly how tall it is when arranged.
EDIT:
Actually, this is a known bug with Silverlight.
You would have to use the SizeChanged event like so:
<UserControl x:Class="SilverlightApplication3.MainPage"
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" d:DesignHeight="300" d:DesignWidth="400">
<Grid x:Name="LayoutRoot" Background="White">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Button Content="Add Item" Click="Button_Click" />
<ListBox x:Name="listBox1" Grid.Column="0" Grid.Row="1" VerticalAlignment="Top" SizeChanged="listBox1_SizeChanged" />
<ListBox x:Name="listBox2" Grid.Column="1" Grid.Row="1" VerticalAlignment="Top" />
</Grid>
</UserControl>
With a code-behind of:
using System.Windows;
using System.Windows.Controls;
namespace SilverlightApplication3 {
public partial class MainPage : UserControl {
public MainPage() {
InitializeComponent();
}
private int counter;
private void Button_Click(object sender, RoutedEventArgs e) {
this.counter++;
this.listBox1.Items.Add(counter.ToString());
}
private void listBox1_SizeChanged(object sender, SizeChangedEventArgs e) {
this.listBox2.Height = this.listBox1.ActualHeight;
}
}
}
You could probably wrap this up into a nice attached behavior also.
EDIT:
Here is such a behavior:
<UserControl x:Class="SilverlightApplication3.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:SilverlightApplication3"
mc:Ignorable="d" d:DesignHeight="300" d:DesignWidth="400">
<Grid x:Name="LayoutRoot" Background="White">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Button Content="Add Item" Click="Button_Click" />
<ListBox x:Name="listBox1" Grid.Column="0" Grid.Row="1" VerticalAlignment="Top" />
<ListBox x:Name="listBox2" Grid.Column="1" Grid.Row="1" VerticalAlignment="Top" local:SizeSynchronizationBehavior.HeightElement="{Binding ElementName=listBox1}" />
</Grid>
</UserControl>
With a code-behind of:
using System;
using System.Windows;
using System.Windows.Controls;
namespace SilverlightApplication3 {
public partial class MainPage : UserControl {
public MainPage() {
InitializeComponent();
}
private int counter;
private void Button_Click(object sender, RoutedEventArgs e) {
this.counter++;
this.listBox1.Items.Add(counter.ToString());
}
}
public static class SizeSynchronizationBehavior {
#region Dependency Properties
///////////////////////////////////////////////////////////////////////////////////
// HeightElement
///////////////////////////////////////////////////////////////////////////////////
/// <summary>
/// Identifies the <c>HeightElement</c> attached dependency property. This field is read-only.
/// </summary>
/// <value>The identifier for the <c>HeightElement</c> attached dependency property.</value>
public static readonly DependencyProperty HeightElementProperty = DependencyProperty.RegisterAttached("HeightElement",
typeof(FrameworkElement), typeof(SizeSynchronizationBehavior), new PropertyMetadata(null, OnHeightElementPropertyValueChanged));
/// <summary>
/// Gets the value of the <see cref="HeightElementProperty"/> attached property for the specified <see cref="FrameworkElement"/>.
/// </summary>
/// <param name="obj">The object to which the attached property is retrieved.</param>
/// <returns>
/// The value of the <see cref="HeightElementProperty"/> attached property for the the specified <see cref="FrameworkElement"/>.
/// </returns>
public static FrameworkElement GetHeightElement(FrameworkElement obj) {
if (obj == null) throw new ArgumentNullException("obj");
return (FrameworkElement)obj.GetValue(HeightElementProperty);
}
/// <summary>
/// Sets the value of the <see cref="HeightElementProperty"/> attached property to the specified <see cref="FrameworkElement"/>.
/// </summary>
/// <param name="obj">The object to which the attached property is written.</param>
/// <param name="value">
/// The new value of the <see cref="HeightElementProperty"/> attached property to the specified <see cref="FrameworkElement"/>.
/// </param>
public static void SetHeightElement(FrameworkElement obj, FrameworkElement value) {
if (obj == null) throw new ArgumentNullException("obj");
obj.SetValue(HeightElementProperty, value);
}
/// <summary>
/// Called when <see cref="HeightElementProperty"/> is changed.
/// </summary>
/// <param name="d">The dependency object that was changed.</param>
/// <param name="e">The <see cref="DependencyPropertyChangedEventArgs"/> instance containing the event data.</param>
private static void OnHeightElementPropertyValueChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) {
FrameworkElement element = d as FrameworkElement;
if (element == null)
return;
SizeChangedEventHandler heightSizeChangedEventHandler = GetSizeChangedEventHandler(element);
if (heightSizeChangedEventHandler == null) {
heightSizeChangedEventHandler = (sender, eventArgs) => {
FrameworkElement he = GetHeightElement(element);
if (he != null)
element.Height = he.ActualHeight;
};
SetSizeChangedEventHandler(element, heightSizeChangedEventHandler);
}
FrameworkElement heightElement = e.OldValue as FrameworkElement;
if (heightElement != null)
heightElement.SizeChanged += heightSizeChangedEventHandler;
heightElement = e.NewValue as FrameworkElement;
if (heightElement != null)
heightElement.SizeChanged += heightSizeChangedEventHandler;
}
///////////////////////////////////////////////////////////////////////////////////
// SizeChangedEventHandler
///////////////////////////////////////////////////////////////////////////////////
/// <summary>
/// Identifies the <c>SizeChangedEventHandler</c> attached dependency property. This field is read-only.
/// </summary>
/// <value>The identifier for the <c>SizeChangedEventHandler</c> attached dependency property.</value>
private static readonly DependencyProperty SizeChangedEventHandlerProperty = DependencyProperty.RegisterAttached("SizeChangedEventHandler",
typeof(SizeChangedEventHandler), typeof(SizeSynchronizationBehavior), new PropertyMetadata(null));
/// <summary>
/// Gets the value of the <see cref="SizeChangedEventHandlerProperty"/> attached property for the specified <see cref="FrameworkElement"/>.
/// </summary>
/// <param name="obj">The object to which the attached property is retrieved.</param>
/// <returns>
/// The value of the <see cref="SizeChangedEventHandlerProperty"/> attached property for the the specified <see cref="FrameworkElement"/>.
/// </returns>
private static SizeChangedEventHandler GetSizeChangedEventHandler(FrameworkElement obj) {
if (obj == null) throw new ArgumentNullException("obj");
return (SizeChangedEventHandler)obj.GetValue(SizeChangedEventHandlerProperty);
}
/// <summary>
/// Sets the value of the <see cref="SizeChangedEventHandlerProperty"/> attached property to the specified <see cref="FrameworkElement"/>.
/// </summary>
/// <param name="obj">The object to which the attached property is written.</param>
/// <param name="value">
/// The new value of the <see cref="SizeChangedEventHandlerProperty"/> attached property to the specified <see cref="FrameworkElement"/>.
/// </param>
private static void SetSizeChangedEventHandler(FrameworkElement obj, SizeChangedEventHandler value) {
if (obj == null) throw new ArgumentNullException("obj");
obj.SetValue(SizeChangedEventHandlerProperty, value);
}
#endregion // Dependency Properties
}
}
I have created a simple project which replicates the problem you are experiencing:
Added a listbox and button
Hooked the button to the height of the listbox
Set the listbox height to "Auto"
Created another button which adds a random amount of items to the Listbox
On run, the listbox grows, but the associated button does not. I am now working to determine why this fails.
UPDATE
Okay, the reason why this does not work is that Auto does not return any actual value that can be used to determine the height of the listbox by this binding. I tried every other approach to solve this without success. It will be interesting to see the resolution of this problem while just relying on binding.
Related
I am writing a little application using WPF. I want to make the window's inner bits transparent, with opaque controls, while the title bar (and the ability to move, minimize, maximize, resize, close etc) remains solid.
However for some reason WPF doesn't allow me to use the default title bar when setting AllowTransparency to true, forcing me to set WindowStyle to None, which isn't what I want. Is there a workaround for this?
My application and problem aren't so advanced that starting from scratch is an issue.
To achieve window transparency, you need to set the following
WindowStartupLocation="CenterScreen"
AllowsTransparency ="True"
WindowStyle="None"
Background="Transparent"
Maximize, minimize, and close can be achieved by yourself:
XAML:
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="30"></RowDefinition>
<RowDefinition></RowDefinition>
</Grid.RowDefinitions>
<Rectangle Fill="Brown" MouseLeftButtonDown="Rectangle_MouseLeftButtonDown"></Rectangle>
<WrapPanel HorizontalAlignment="Right" VerticalAlignment="Top" Height="30" Width="Auto">
<Button Width="20" Height="20" Margin="5" Click="Button_Click_1">_</Button>
<Button Width="20" Height="20" Margin="5" Click="Button_Click_2">口</Button>
<Button Width="20" Height="20" Margin="5" Click="Button_Click_3">X</Button>
</WrapPanel>
</Grid>
Code:
/// <summary>
/// Min
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void Button_Click_1(object sender, RoutedEventArgs e)
{
if (this.WindowState != WindowState.Minimized)
{
this.WindowState = WindowState.Minimized;
}
}
/// <summary>
/// Max
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void Button_Click_2(object sender, RoutedEventArgs e)
{
if (this.WindowState != WindowState.Maximized)
{
this.WindowState = WindowState.Normal;
}
}
/// <summary>
/// Close
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void Button_Click_3(object sender, RoutedEventArgs e)
{
this.Close();
}
/// <summary>
/// DragMove
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void Rectangle_MouseLeftButtonDown(object sender, System.Windows.Input.MouseButtonEventArgs e)
{
this.DragMove();
}
Here is an improved version of the maximise code
private void Button_Click_2(object sender, RoutedEventArgs e)
{
if (this.WindowState != WindowState.Maximized)
{
this.WindowState = WindowState.Maximized;
} else if(this.WindowState != WindowState.Normal)
{
this.WindowState = WindowState.Normal;
}
}
The View : I have UserControl, which has a TextBox and a Label. When the "Enter" key is down, I want the Label to be updated with the value form the text box. For the sake of this example, I created a CarUserControl. I will be hosting a list of these in an ItemsControl in the MainWindow.
The Model : I have class Car, which will be the model.
The ViewModel : I do not have a ViewModel for the CarUserControl and the Car. I have one for the MainWindow instead - lets call this the MainViewModel.
I can get the commands propogated from the individual usercontrols to the MainViewModel, but I'm unsure about getting the values from the textboxes in the MainViewModel?
Here are some of the assumptions I'm making from what I`ve read online about MVVM (there are ofcourse some sources which say the assumptions are wrong).
1] Usercontrols should not have a ViewModel.
2] Usercontrols should only expose dependency properties, and not public properties with INotifyChanged or events.
So, the question is, how do I update the label, and access the TextBox value in the MainViewModel.
Here is the test code :
-----CarUserControl.xaml----
<UserControl x:Class="TestMVVM.CarUserControl"
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:TestMVVM"
mc:Ignorable="d"
d:DesignHeight="50" d:DesignWidth="300" x:Name="thisUC">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Label Grid.Column="0">--</Label>
<TextBox Grid.Column="1" Background="#FFE8D3D3" BorderThickness="0">
<TextBox.InputBindings>
<KeyBinding Key="Enter"
Command="{Binding KeyDownCommand, ElementName=thisUC}"
CommandParameter="{Binding}"/>
</TextBox.InputBindings>
</TextBox>
</Grid>
</UserControl>
-----CarUserControl.cs-----
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
namespace TestMVVM
{
/// <summary>
/// Interaction logic for CarUserControl.xaml
/// The Usercontrol
/// </summary>
public partial class CarUserControl : UserControl
{
private static readonly DependencyProperty StrValueProperty = DependencyProperty.Register("StrValue", typeof(float), typeof(CarUserControl), new PropertyMetadata(null));
private static readonly DependencyProperty KeyDownCommandProperty = DependencyProperty.Register("KeyDownCommand", typeof(ICommand), typeof(CarUserControl), new PropertyMetadata(null)); //Enter key down in the text box
public CarUserControl()
{
InitializeComponent();
}
public string StrValue
{
get { return (string)GetValue(StrValueProperty); }
set { SetValue(StrValueProperty, value); }
}
/// <summary>
/// "Enter" key down
/// </summary>
public ICommand KeyDownCommand
{
get { return (ICommand)GetValue(KeyDownCommandProperty); }
set { SetValue(KeyDownCommandProperty, value); }
}
}
}
//---The Model--Car.cs
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace TestMVVM
{
/// <summary>
/// A simple model
/// </summary>
class Car : INotifyPropertyChanged
{
public Car(string name) {
this.name = name;
}
private string name;
public event PropertyChangedEventHandler PropertyChanged;
public string Name
{
get { return name; }
set
{
name = value;
OnPropertyChanged("Name");
}
}
public void OnPropertyChanged(string name)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(name));
}
}
}
}
-----Main View Model---
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Input;
namespace TestMVVM
{
/// <summary>
/// The Main View Model
/// </summary>
class MainViewModel : INotifyPropertyChanged
{
/// <summary>
/// The main view model
/// </summary>
public MainViewModel()
{
//Create some test data
cars = new ObservableCollection<Car>();
cars.Add(new Car("Audi"));
cars.Add(new Car("Toyota"));
cars.Add(new Car("Subaru"));
cars.Add(new Car("Volvo"));
}
public event PropertyChangedEventHandler PropertyChanged;
private ObservableCollection<Car> cars; //List of tensioner spools
private ICommand enterDownCommand;
public ObservableCollection<Car> Cars
{
get { return cars; }
set
{
cars = value;
OnPropertyChanged("Cars");
}
}
public ICommand EnterDownCommand
{
get
{
if (enterDownCommand == null)
{
enterDownCommand = new RelayMCommand<Car>(OnEnterDownCommand);
}
return enterDownCommand;
}
}
/// <summary>
/// Called when "Enter" key is down.
/// </summary>
/// <param name="obj"></param>
private void OnEnterDownCommand(Car obj)
{
//How do I get the text box value here?
Console.Write(">>"+obj.Name);
}
public void OnPropertyChanged(string name)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(name));
}
}
}
}
-----MainWindow---
<Window x:Class="TestMVVM.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:TestMVVM"
mc:Ignorable="d"
Title="MainWindow" Height="350" Width="525">
<Window.DataContext>
<local:MainViewModel x:Name ="MainVM"/>
</Window.DataContext>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Grid Grid.Row="0">
<Viewbox>
<ItemsControl ItemsSource="{Binding Cars}" Margin="5" Width="200">
<ItemsControl.ItemTemplate>
<DataTemplate>
<local:CarUserControl Margin="5"
KeyDownCommand="{Binding Path=DataContext.EnterDownCommand, RelativeSource={RelativeSource AncestorType=ItemsControl}}"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Vertical" IsItemsHost="True" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>
</Viewbox>
</Grid>
</Grid>
</Window>
---Relay Command---
using System;
using System.Threading;
using System.Windows.Input;
namespace TestMVVM
{
/// <summary>
/// Same as the Relay Command, except this handles an array of generic type <T>
/// </summary>
/// <typeparam name="T">Generic type parameter</typeparam>
public class RelayMCommand<T> : ICommand
{
private Predicate<T> _canExecute;
private Action<T> _execute;
public RelayMCommand(Action<T> execute, Predicate<T> canExecute = null)
{
_execute = execute;
_canExecute = canExecute;
}
private void Execute(T parameter)
{
_execute(parameter);
}
private bool CanExecute(T parameter)
{
return _canExecute == null ? true : _canExecute(parameter);
}
public bool CanExecute(object parameter)
{
return parameter == null ? false : CanExecute((T)parameter);
}
public void Execute(object parameter)
{
_execute((T)parameter);
}
public event EventHandler CanExecuteChanged;
public void RaiseCanExecuteChanged()
{
var temp = Volatile.Read(ref CanExecuteChanged);
if (temp != null)
{
temp(this, new EventArgs());
}
}
}
}
A UserControl may inherit its DataContext from a parent window or the current item in an ItemsControl.
So if you bind your ItemsControl to an IEnumerable<Car>, each instance of the CarUserControl can bind directly to the Name property of the corresponding Car object:
<TextBox Text="{Binding Name}"
Grid.Column="1" Background="#FFE8D3D3" BorderThickness="0">
<TextBox.InputBindings>
<KeyBinding Key="Enter"
Command="{Binding KeyDownCommand, ElementName=thisUC}"
CommandParameter="{Binding}"/>
</TextBox.InputBindings>
</TextBox>
This is because the UserControl automatically inherits the DataContext from its parent element which is the corresponding Car object in the ItemsControl in this case.
I have a button and want to change the click handler every time I change tabs. I was hoping to perform this with Binding.
<Window x:Class="BWCRenameUtility.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:vpan="clr-namespace:BWCRenameUtility.View.VersionPanels"
Title="MainWindow" Height="526" Width="525">
<Grid>
<DockPanel>
<TextBlock Text="Foo" DockPanel.Dock="Top" TextWrapping="Wrap" Padding="10" />
<Grid DockPanel.Dock="Bottom">
<!-- This is not correct, how do I perform this binding correct? -->
<Button Content="Export..." HorizontalAlignment="Right" Margin="10" Click="{Binding SelectedItem.Content.PerformExport,ElementName=tabcontrol}" />
</Grid>
<TabControl Name="tabcontrol">
<TabItem Header="1.2.5">
<vpan:VersionPanel1_2_5/>
</TabItem>
<TabItem Header="1.2.8">
<vpan:VersionPanel1_2_8/> <!-- These can be of the same Type by inheritance -->
</TabItem>
</TabControl>
</DockPanel>
</Grid>
</Window>
As you can see, the Button.Click is not bound correctly and I want to know how this works in WPF.
You can achieve this with Commands, you will create a ICommand for each of toy TabItem ViewModels and bind the Buttons Command property to that Command
The RelayCommand is a very common way to handle stuff like this and can be used throughout your application
Relay command:
public class RelayCommand : ICommand
{
#region Fields
readonly Action<object> _execute;
readonly Predicate<object> _canExecute;
#endregion
#region Constructors
/// <summary>
/// Initializes a new instance of the <see cref="RelayCommand"/> class.
/// </summary>
/// <param name="execute">The execute.</param>
public RelayCommand(Action<object> execute) : this(execute, null) { }
/// <summary>
/// Initializes a new instance of the <see cref="RelayCommand"/> class.
/// </summary>
/// <param name="execute">The action to execute.</param>
/// <param name="canExecute">The can execute.</param>
/// <exception cref="System.ArgumentNullException">execute</exception>
public RelayCommand(Action<object> execute, Predicate<object> canExecute)
{
if (execute == null)
throw new ArgumentNullException("execute");
_execute = execute;
_canExecute = canExecute;
}
#endregion
#region ICommand Members
/// <summary>
/// Defines the method that determines whether the command can execute in its current state.
/// </summary>
/// <param name="parameter">Data used by the command. If the command does not require data to be passed, this object can be set to null.</param>
/// <returns>
/// true if this command can be executed; otherwise, false.
/// </returns>
[DebuggerStepThrough]
public bool CanExecute(object parameter)
{
return _canExecute == null ? true : _canExecute(parameter);
}
/// <summary>
/// Occurs when changes occur that affect whether or not the command should execute.
/// </summary>
public event EventHandler CanExecuteChanged
{
add { CommandManager.RequerySuggested += value; }
remove { CommandManager.RequerySuggested -= value; }
}
/// <summary>
/// Defines the method to be called when the command is invoked.
/// </summary>
/// <param name="parameter">Data used by the command. If the command does not require data to be passed, this object can be set to null.</param>
public void Execute(object parameter)
{
_execute(parameter);
}
#endregion
}
And you would use in the following fashion in your application
ViewModel or Control:
public class VersionPanel1_2_8 : VersionPanel
{
public ICommand MyCommand { get; internal set; }
public VersionPanel1_2_8()
{
MyCommand = new RelayCommand(x => MethodToExecute());
}
private void MethodToExecute()
{
}
}
Xaml:
<Window x:Class="BWCRenameUtility.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:vpan="clr-namespace:BWCRenameUtility.View.VersionPanels"
Title="MainWindow" Height="526" Width="525">
<Grid>
<DockPanel>
<TextBlock Text="Foo" DockPanel.Dock="Top" TextWrapping="Wrap" Padding="10" />
<Grid DockPanel.Dock="Bottom">
<!-- This is not correct, how do I perform this binding correct? -->
<Button Content="Export..." HorizontalAlignment="Right" Margin="10" Command="{Binding SelectedItem.Content.MyCommand,ElementName=tabcontrol}" />
</Grid>
<TabControl Name="tabcontrol">
<TabItem Header="1.2.5">
<vpan:VersionPanel1_2_5/>
</TabItem>
<TabItem Header="1.2.8">
<vpan:VersionPanel1_2_8/> <!-- These can be of the same Type by inheritance -->
</TabItem>
</TabControl>
</DockPanel>
</Grid>
</Window>
You need to bind Button's Command property to your command in WPF like,
Command="{Binding SelectedItem.Content.PerformExport, ElementName=tabcontrol}"
Click is an event, if you want you can also do event to command binding (binding any event to command in your viewmodel) however that is not necessary in your case
I am creating a user control, inside the control I have a Label Control. (for example: an email control, contains a label and a text box).
In the email address control, I defined a Title property, which when the user changes the property value, I want to set the string value to the Label.
bellow is the XAML and Code:
XAML:
<UserControl x:Class="SIMind.ClinicManagement.GUI.Controls.CommonControl.EmailAddressCtrl"
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="32" MinHeight="32" MaxHeight="32" Width="386" MinWidth="386">
<Grid>
<Grid.RowDefinitions>
<RowDefinition />
</Grid.RowDefinitions>
<StackPanel HorizontalAlignment="Stretch" Margin="0" Name="stackPanel1" VerticalAlignment="Stretch" Orientation="Horizontal">
<Label Content="Email :" Height="24" HorizontalContentAlignment="Right" Name="lblEmailCaption" Tag="PhoneType" Width="140" />
<TextBox MaxLength="100" Name="txtEmail" Text="email#server.com" Width="240" Margin="1" Height="23" />
</StackPanel>
</Grid>
</UserControl>
Code:
namespace SIMind.ClinicManagement.GUI.Controls.CommonControl
{
/// <summary>
/// Interaction logic for EmailAddressCtrl.xaml
/// </summary>
public partial class EmailAddressCtrl : UserControl
{
public EmailAddressCtrl()
{
InitializeComponent();
}
#region Dependency Property
#region Title Property
/// <summary>
/// Title property
/// </summary>
public static readonly DependencyProperty TitleProperty =
DependencyProperty.Register("Title", typeof(String),
typeof(EmailAddressCtrl), new PropertyMetadata("Phone Number :"));
/// <summary>
/// Gets and Sets the main label of the control.
/// </summary>
public string Title
{
get { return (string)GetValue(TitleProperty); }
set {
SetValue(TitleProperty, value);
lblEmailCaption.Content = value;
}
}
#endregion
#endregion
}
}
But it seems not working the way I wanted it to be: The dependency property is set, but the label is not refreshed to be the property set.
Any one got a good answer? :-)
Hope this will work for you
<Label Content="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type EmailAddressCtrl}},Path=Title}" Height="24" HorizontalContentAlignment="Right" Name="lblEmailCaption" Tag="PhoneType" Width="140" />
I have a Grid element that has two columns and three rows. The last row has a height of 0... and I animate the height property using a custom animation class because the gridheight property isnt an integer..
The animation works just fine, but when I activate it, it changes the width of the second column seemingly randomly.. sometimes just a few pixels bigger and sometimes over double as wide...
Here is the grid code
<Grid >
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition Width="50"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="1*" />
<RowDefinition Height="7"/>
<RowDefinition Name="LyricsRow" Height="1">
<RowDefinition.Style>
<Style>
<Style.Triggers>
<DataTrigger Binding="{Binding Path=IsTrayOpen}" Value="True">
<DataTrigger.EnterActions>
<BeginStoryboard>
<Storyboard>
<local:GridLengthAnimation
Storyboard.TargetProperty="Height"
From="0" To="150" Duration="0:0:0.3" >
</local:GridLengthAnimation>
</Storyboard>
</BeginStoryboard>
</DataTrigger.EnterActions>
<DataTrigger.ExitActions>
<BeginStoryboard>
<Storyboard>
<local:GridLengthAnimation
Storyboard.TargetProperty="Height"
From="150" To="0" Duration="0:0:0.5" />
</Storyboard>
</BeginStoryboard>
</DataTrigger.ExitActions>
</DataTrigger>
</Style.Triggers>
</Style>
</RowDefinition.Style>
</RowDefinition>
</Grid.RowDefinitions>
Is there any reasons this could be going on?
For those of you that might be wondering about the GridLengthAnimation implementation mentioned in the question, here is one (from http://social.msdn.microsoft.com/forums/en-US/wpf/thread/da47a4b8-4d39-4d6e-a570-7dbe51a842e4/)
/// <summary>
/// Animates a grid length value just like the DoubleAnimation animates a double value
/// </summary>
public class GridLengthAnimation : AnimationTimeline
{
/// <summary>
/// Returns the type of object to animate
/// </summary>
public override Type TargetPropertyType
{
get
{
return typeof(GridLength);
}
}
/// <summary>
/// Creates an instance of the animation object
/// </summary>
/// <returns>Returns the instance of the GridLengthAnimation</returns>
protected override System.Windows.Freezable CreateInstanceCore()
{
return new GridLengthAnimation();
}
/// <summary>
/// Dependency property for the From property
/// </summary>
public static readonly DependencyProperty FromProperty = DependencyProperty.Register("From", typeof(GridLength),
typeof(GridLengthAnimation));
/// <summary>
/// CLR Wrapper for the From depenendency property
/// </summary>
public GridLength From
{
get
{
return (GridLength)GetValue(GridLengthAnimation.FromProperty);
}
set
{
SetValue(GridLengthAnimation.FromProperty, value);
}
}
/// <summary>
/// Dependency property for the To property
/// </summary>
public static readonly DependencyProperty ToProperty = DependencyProperty.Register("To", typeof(GridLength),
typeof(GridLengthAnimation));
/// <summary>
/// CLR Wrapper for the To property
/// </summary>
public GridLength To
{
get
{
return (GridLength)GetValue(GridLengthAnimation.ToProperty);
}
set
{
SetValue(GridLengthAnimation.ToProperty, value);
}
}
/// <summary>
/// Animates the grid let set
/// </summary>
/// <param name="defaultOriginValue">The original value to animate</param>
/// <param name="defaultDestinationValue">The final value</param>
/// <param name="animationClock">The animation clock (timer)</param>
/// <returns>Returns the new grid length to set</returns>
public override object GetCurrentValue(object defaultOriginValue,
object defaultDestinationValue, AnimationClock animationClock)
{
double fromVal = ((GridLength)GetValue(GridLengthAnimation.FromProperty)).Value;
//check that from was set from the caller
if (fromVal == 1)
//set the from as the actual value
fromVal = ((GridLength)defaultDestinationValue).Value;
double toVal = ((GridLength)GetValue(GridLengthAnimation.ToProperty)).Value;
if (fromVal > toVal)
return new GridLength((1 - animationClock.CurrentProgress.Value) * (fromVal - toVal) + toVal, GridUnitType.Star);
else
return new GridLength(animationClock.CurrentProgress.Value * (toVal - fromVal) + fromVal, GridUnitType.Star);
}
}
It probably depends on the content of the "cells"
try setting MaxWidth="50" on your columnDefinition in addition to Width="50"