How to bind a WPF control to the code behind? - wpf

I have this XAML:
<Window x:Class="WpfBindToCodeBehind.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Window1" Height="300" Width="300"
Loaded="Window_Loaded">
<StackPanel Orientation="Vertical">
<Button Name="ToggleExpand" Click="ToggleExpand_Click">Toggle Expander</Button>
<Expander Name="Expander"
Header="Don't click me, click the button!"
IsExpanded="{Binding RelativeSource={RelativeSource Self},Path=MayExpand}">
<TextBlock Text="{Binding}"/>
</Expander>
</StackPanel>
</Window>
This is the code behind:
public partial class Window1 : Window,INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public Window1()
{
InitializeComponent();
}
private void ToggleExpand_Click(object sender, RoutedEventArgs e)
{
MayExpand = !mayExpand;
}
private void Window_Loaded(object sender, RoutedEventArgs e)
{
Expander.DataContext = "Show me";
}
private bool mayExpand;
public bool MayExpand
{
get { return mayExpand; }
set
{
mayExpand = value;
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs("MayExpand"));
}
}
}
The Binding expression for the IsExpanded property is not working. This code is a simplification, in reality the expander's binding is already set through the datacontent of a parent control.
How can I bind the IsExpanded property to a property of the code behind?
Can I bind it to the result of a method in the code behind?

The source for the binding is a RelativeSource.Self. That means the source is the Expander rather than the Window. Something like this will work:
IsExpanded="{Binding MayExpand, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}}"
You could also use a name to simplify things:
<Window x:Name="_root">
<Expander IsExpanded="{Binding MayExpand, ElementName=_root}"/>

Related

Set a callback method in a DataTemplate

I have a listbox of custom items set through a DataTemplate.
<UserControl x:Name="MyMainControl">
<ListBox x:Name="lbConfigurationList"
DataContext="{Binding DataContext, RelativeSource={RelativeSource AncestorType={x:Type Window}}}"
ItemsSource="{Binding ConfListVM.ObservableConfList}"
SelectionChanged="OnConfigurationSelected"
<ListBox.ItemTemplate>
<DataTemplate>
<local:EditableTextBlock Text="{Binding ConfName}" />
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</UserControl>
Is it possible to call a method on the control owning this listbox each time someting happens in the EditableTextBlock, through something like:
<local:EditableTextBlock Text="{Binding ConfName}" MyNotifyEvent={SomeMyMainControlMethod} />
If this is possible, which terms should I search to understand how to setup and launch the event from my DataTemplate's EditableTextBlock?
Thanks in advance
Here is a full example using Behaviors:
<Window x:Class="TextBlockEventHandlerInDataTemplate.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
xmlns:local="clr-namespace:TextBlockEventHandlerInDataTemplate"
Title="MainWindow" Height="350" Width="525">
<Grid>
<ListBox ItemsSource="{Binding items}">
<ListBox.ItemTemplate>
<DataTemplate>
<TextBox Text="{Binding ., Mode=TwoWay}" >
<i:Interaction.Behaviors>
<local:ShowMessageOnTextChangedBehavior/>
</i:Interaction.Behaviors>
</TextBox>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Grid>
Codebehind, sorry i didn't have time for MVVM approach:
public partial class MainWindow : Window
{
public ObservableCollection<string> items { get; set; }
public MainWindow()
{
InitializeComponent();
items = new ObservableCollection<string>();
items.Add("item 1");
items.Add("item 2");
items.Add("item 3");
items.Add("item 4");
this.DataContext = this;
}
}
And this should be your class doing the job:
public class ShowMessageOnTextChangedBehavior : Behavior<TextBox>
{
protected override void OnAttached()
{
AssociatedObject.TextChanged += AssociatedObjectOTextChanged;
base.OnAttached();
}
protected override void OnDetaching()
{
AssociatedObject.TextChanged -= AssociatedObjectOTextChanged;
base.OnDetaching();
}
private void AssociatedObjectOTextChanged(object sender, RoutedEventArgs routedEventArgs)
{
MessageBox.Show(AssociatedObject.Text);
}
}
The other thing would be to define an attached property. I use both ways, depending on the mood :)
Ah, i was about to forget an important thing: you need to reference
Microsoft.Expression.Interactions and System.Windows.Interactivity from Expression.Blend.SDK.

WPF One Way Binding broken

Im trying to bind 2 different WPF controls to the same property in the ViewModel, a CheckBox.IsChecked and an Expander.IsExpanded. The behavior I want to achieve is to have the CheckBox affect the ViewModel (and therefore the Expander as well), but not the other way bound.
Something like:
Checkbox Checked -> ViewModel property set to frue -> Expander.Expand
Checkbox Unchecked -> ViewModel property set to false -> Expander.Collapse
Expander Expanded -> Nothing else affected
Expander Collapsed -> Nothing else affected
Here's the XAML:
<Window x:Class="WpfApplication9.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">
<Expander IsExpanded="{Binding IsChecked, Mode=OneWay}">
<Expander.Header>
<CheckBox IsChecked="{Binding IsChecked}" Content="Is Checked"/>
</Expander.Header>
<TextBlock Text="Expanded!"/>
</Expander>
</Window>
and the Code:
using System.ComponentModel;
using System.Windows;
namespace WpfApplication9
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
DataContext = new ViewModel();
}
}
public class ViewModel: INotifyPropertyChanged
{
private bool _isChecked;
public bool IsChecked
{
get { return _isChecked; }
set
{
_isChecked = value;
NotifyPropertyChange("IsChecked");
}
}
protected void NotifyPropertyChange(string PropertyName)
{
PropertyChanged(this, new PropertyChangedEventArgs(PropertyName));
}
public event PropertyChangedEventHandler PropertyChanged = delegate { };
}
}
Now my problem is, as soon as I click on the Expander to expand / collapse it, the Binding seems to stop working. Can anyone explain to me why this is happening and how do I achieve this? Thanks in advance!
New Answer
Discovered you could do this by setting your UpdateSourceTrigger to Explicit on your Expander. This keeps the binding as Two-Way, but never updates the Source since you're telling it not to update the source unless you explicitly tell it to.
<Expander IsExpanded="{Binding IsChecked, UpdateSourceTrigger=Explicit}">
<Expander.Header>
<CheckBox IsChecked="{Binding IsChecked}" Content="Is Checked"/>
</Expander.Header>
<TextBlock Text="Expanded!"/>
</Expander>
Leaving my old answer below so the comments make sense, and because I still feel there is no problem with view-specific code going in the code-behind of a view :)
Old Answer
Personally since this is View-Specific code, I see no problem with using a CheckBox click event to set the Expander's IsExpanded value.
private void MyCheckBox_Click(object sender, RoutedEventArgs e)
{
MyExpander.IsExpanded = ((CheckBox)sender).IsChecked.GetValueOrDefault();
}
You could make this even more generic by removing the names and navigating the Visual Tree to find the Expander associated with the CheckBox. Here's an example using some Visual Tree Helpers I built
private void CheckBox_Click(object sender, RoutedEventArgs e)
{
var chk = (CheckBox)sender;
var expander = VisualTreeHelpers.FindAncestor<Expander>(chk);
if (expander != null)
expander.IsExpanded = chk.IsChecked.GetValueOrDefault();
}
If you want the checkbox to affect the expander (but not vice versa) then bind the expander normally and use OneWayToSource on the checkbox:
<Window x:Class="WpfApplication9.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">
<Expander IsExpanded="{Binding IsChecked}">
<Expander.Header>
<CheckBox IsChecked="{Binding IsChecked, Mode=OneWayToSource}" Content="Is Checked"/>
</Expander.Header>
<TextBlock Text="Expanded!"/>
</Expander>
</Window>
Using OneWayToSource on the checkbox will allow it to:
modify the underlying property (and therefore affect the expander, which is also bound to that property)
not be affected by other components that make changes to the underlying property
If you'd like to avoid any code-behind, you can add a degree of separation between the Expander and CheckBox states in your ViewModel:
private bool _isChecked;
public bool IsChecked
{
get { return _isChecked; }
set
{
_isChecked = value;
NotifyPropertyChange("IsChecked");
IsExpanded = value;
}
}
private bool _isExpanded;
public bool IsExpanded
{
get { return _isExpanded; }
set
{
_isExpanded = value;
NotifyPropertyChange("IsExpanded");
}
}
<Expander IsExpanded="{Binding IsExpanded}">
<Expander.Header>
<CheckBox IsChecked="{Binding IsChecked}" Content="Is Checked" x:Name="cb"/>
</Expander.Header>
<TextBlock Text="Expanded!"/>
</Expander>

Binding a property to the change the listbox items foreground

I have the following ViewModel and I'd like to bind the HotkeysForeground to change the color in the ListBox.
namespace Monkey.Core.ViewModel
{
using System;
using System.Collections.ObjectModel;
using System.Windows.Media;
using Monkey.Core.SystemMonitor.Accessibility;
using Monkey.Core.SystemMonitor.Input;
public class MainWindowViewModel : WorkspaceViewModel
{
private readonly FocusManager _focusManager;
private readonly HotkeyManager _hotkeyManager;
private readonly ObservableCollection<string> _hotkeys;
private Brush _foregroundColor;
private string _title;
public MainWindowViewModel()
{
_hotkeys = new ObservableCollection<string>();
_hotkeyManager = new HotkeyManager();
_hotkeyManager.NewHotkey += HotkeyManager_NewHotkey;
_focusManager = new FocusManager();
_focusManager.Focus += FocusManager_Focus;
}
public Brush HotkeysForeground
{
get
{
return _foregroundColor;
}
set
{
_foregroundColor = value;
OnPropertyChanged(() => Title);
}
}
public ReadOnlyObservableCollection<string> Hotkeys
{
get
{
return new ReadOnlyObservableCollection<string>(_hotkeys);
}
}
public string Title
{
get
{
return _title;
}
set
{
_title = value;
OnPropertyChanged(() => Title);
}
}
protected override void OnDispose()
{
base.OnDispose();
_hotkeyManager.Dispose();
_focusManager.Dispose();
}
private void FocusManager_Focus(object sender, FocusManagerEventArgs e)
{
Title = e.Title;
}
private void HotkeyManager_NewHotkey(object sender, EventArgs eventArgs)
{
HotkeysForeground = Brushes.Blue;
_hotkeys.Clear();
foreach (var hotkey in _hotkeyManager.GetHotkeys())
{
_hotkeys.Add(hotkey);
}
}
}
}
I want to change the foreground color of the items in the ListBox every time the "HotkeyManager_NewHotkey" is fired, to some reason I can't seems to bind it to the view, I tried multiple things to make it work to no avail.
Here is the View I have.
<Window x:Class="Monkey.View.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="{Binding Mode=OneWay, Path=Title, UpdateSourceTrigger=PropertyChanged}" Height="200" Width="200" ShowInTaskbar="False" WindowStyle="ToolWindow" Topmost="True" ResizeMode="CanResizeWithGrip" AllowsTransparency="False">
<ListBox
Canvas.Left="110"
Canvas.Top="74"
Name="HotkeyList"
Height="Auto" Width="Auto" HorizontalContentAlignment="Left"
BorderThickness="0"
ScrollViewer.CanContentScroll="False"
ScrollViewer.HorizontalScrollBarVisibility="Disabled"
ScrollViewer.VerticalScrollBarVisibility="Disabled"
ItemsSource="{Binding Path=Hotkeys}" VerticalAlignment="Stretch" VerticalContentAlignment="Center" FontSize="20">
<ListBox.ItemContainerStyle>
<Style TargetType="ListBoxItem">
<Setter Property="IsEnabled" Value="False" />
</Style>
</ListBox.ItemContainerStyle>
</ListBox>
</Window>
I'm fairly new to WPF and haven't really explored bindings into depth so any help is appreciated.
For starters, you're notifying about Title property change in HotkeysForeground. However, this might not be the case.
If fixing that doesn't help, this rather lenghty way should work for you:
change HotkeysForeground property type to string (just store color name)
create static resource brush in XAML, bind it to color name
override listbox item template to something fairly simple (eg. Label) and bind its foreground to previously mentioned bursh
So, applying those changes:
public string HotkeysForeground
{
get { return _foregroundColor; }
set
{
_foregroundColor = value;
// I assume this is some smart workaround to INPC...
OnPropertyChanged(() => HotkeysForeground);
}
}
Now, in XAML you'll have to do this:
<!-- need to import System namespace -->
<Window x:Class="Monkey.View.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:sys="clr-namespace:System;assembly=mscorlib">
<Window.Resources>
<SolidColorBrush Color="{Binding HotkeysForeground}" x:Key="HotkeysBrush"/>
</Window.Resources>
<ListBox ItemsSource="{Binding Path=Hotkeys}">
<ListBox.ItemTemplate>
<!
<DataTemplate DataType="{x:Type sys:String}">
<Label Content="{Binding}"
Foreground="{StaticResource HotkeysBrush}"/>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Window>

Listbox Binding Problem

Please solve my problem, my listbox don't bind, i don't why :(
class TLocation
{
public string name;
}
Main Window:
<Window x:Class="WpfApplication5.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WpfApplication5"
Title="MainWindow" Height="350" Width="525" Loaded="Window_Loaded">
<Window.Resources>
<DataTemplate DataType="{x:Type local:TLocation}" x:Key="myTaskTemplate">
<TextBlock Height="23" HorizontalAlignment="Left" Margin="1" Name="textBlock1" Text="{Binding Path=name, FallbackValue=name}" VerticalAlignment="Top" />
</DataTemplate>
</Window.Resources>
<Grid>
<ListBox Height="131" HorizontalAlignment="Left" Margin="29,44,0,0" Name="listBox1" VerticalAlignment="Top" Width="200" ItemTemplate="{StaticResource myTaskTemplate}" />
</Grid>
</Window>
Main Window Code:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void Window_Loaded(object sender, RoutedEventArgs e)
{
List<TLocation> list = new List<TLocation>();
TLocation temp = new TLocation();
temp.name = "hi";
list.Add(temp);
listBox1.ItemsSource = list;
}
}
name is a field, you can only bind to public properties though. You might also want to implement INotifyPropertyChanged if the name changes at runtime. If you are new to databinding make sure to read the overview.
e.g.
public class TLocation : INotifyPropertyChanged
{
private string _name = null;
public string Name
{
get { return _name; }
set
{
if (_name != value)
{
_name = value;
OnPropertyChanged("Name");
}
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string propertyName)
{
if (this.PropertyChanged != null)
{
this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
(Binding is also case senstitive and your capitalization is not following the conventions so i changed it in my example, here the Binding.Path would need to be Name.)

Dependency propery Binding Problem

Main WINDOW
<Window x:Class="dep2.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:dep2"
Title="Window1" Height="300" Width="381">
<Grid>
<local:UserControl1></local:UserControl1>
<Button Height="23" HorizontalAlignment="Right" Margin="0,0,77,36" Name="button1" VerticalAlignment="Bottom" Width="75" Click="button1_Click">Button</Button>
</Grid>
</Window>
public partial class Window1 : Window
{
UserControl1 uc = new UserControl1();
public Window1()
{
InitializeComponent();
}
private void button1_Click(object sender, RoutedEventArgs e)
{
uc.InfoText = "SAMPLE";
}
}
My User CONTROL
<UserControl x:Class="dep2.UserControl1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Height="32" Width="300">
<Grid Height="30">
<StackPanel Background="LightCyan">
<TextBox Height="21" Name="textBlock1" Width="120" Text="{Binding Text}" />
</StackPanel>
</Grid>
</UserControl>
public partial class UserControl1 : UserControl
{
public string InfoText
{
get
{
return (string)GetValue(InfoTextProperty);
}
set
{
SetValue(InfoTextProperty, value);
}
}
public static readonly DependencyProperty InfoTextProperty =
DependencyProperty.Register(
"InfoText",
typeof(string),
typeof(UserControl1),
new FrameworkPropertyMetadata(
new PropertyChangedCallback(ChangeText)));
private static void ChangeText(DependencyObject source, DependencyPropertyChangedEventArgs e)
{
(source as UserControl1).UpdateText(e.NewValue.ToString());
}
private void UpdateText(string NewText)
{
textBox1.Text = NewText;
}
public UserControl1()
{
InitializeComponent();
DataContext = this;
}
}
Am getting my value in user control dependency property, but i cant able to bind my value to the text box.am using like this to bind Text="{Binding Text}" is it right,or how to bind my value in user control
i have attached my sample project here,
http://cid-08ec3041618e8ee4.skydrive.live.com/self.aspx/.SharedFavorites/dep2.rar
Can any one look and tell whats wrong in that,
everythng working well, but i cant bind the value in text box,
when u click the button u can see the passed value to usercontrol in message box, but i cant bind that value in text box.
Why????
Your code handles the callback from the dependency property and sets the text box value directly. This is not the role of this callback.
And by setting the Text property, you have lost the binding. Local property setting has a higher priority than bindings. See this blog

Resources