I'm trying to bind the window Title attribute so that it will show the filename and modified status for an object. The filename and modified status are both dependency properties on the object.
I know I could probably just add a "WindowTitle" property or such to the object, but that seems rather hacky. I've created a very stripped down version of what I'm trying to do.
Here's the XAML:
<Window x:Class="WpfApplication1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WpfApplication1"
Title="{Binding Converter={StaticResource windowTitleConverter}}" Height="195" Width="245">
<Window.Resources>
<local:WindowTitleConverter x:Key="windowTitleConverter"/>
</Window.Resources>
<Grid Height="150" Width="217">
<TextBox Height="23" HorizontalAlignment="Left" Margin="12,12,0,0" Name="textBox1" VerticalAlignment="Top" Width="120" Text="{Binding FileName}" />
<CheckBox Content="Modified" Height="16" HorizontalAlignment="Left" Margin="12,41,0,0" Name="checkBox1" VerticalAlignment="Top" IsChecked="{Binding Modified}" />
</Grid>
And the Code:
using System;
using System.Globalization;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
namespace WpfApplication1
{
public partial class MainWindow : Wind开发者_StackOverflowow
{
public MainWindow()
{
InitializeComponent();
DataContext = new Foo();
}
}
public class Foo : DependencyObject
{
public string FileName
{
get { return (string)GetValue(FileNameProperty); }
set { SetValue(FileNameProperty, value); }
}
public static readonly DependencyProperty FileNameProperty =
DependencyProperty.Register("FileName", typeof(string), typeof(Foo), new UIPropertyMetadata());
public bool Modified
{
get { return (bool)GetValue(ModifiedProperty); }
set { SetValue(ModifiedProperty, value); }
}
public static readonly DependencyProperty ModifiedProperty =
DependencyProperty.Register("Modified", typeof(bool), typeof(Foo), new UIPropertyMetadata(0));
}
public class WindowTitleConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
Foo foo = (Foo)value;
if (foo == null || foo.FileName == null)
return "Foo";
return foo.FileName + (foo.Modified ? " *" : "");
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotSupportedException();
}
}
}
If you move the Title
binding below the Resources it will work. I'm not sure why the order of declaration is of importance here but it is, seems like a bug to me
<Window x:Class="WpfApplication1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WpfApplication1"
Height="195" Width="245">
<Window.Resources>
<local:WindowTitleConverter x:Key="windowTitleConverter"/>
</Window.Resources>
<Window.Title>
<Binding Converter="{StaticResource windowTitleConverter}"/>
</Window.Title>
<!--...-->
</Window>
Update
The problem you're getting now is because the Dependency Property Modified has the wrong default value type. It is of type bool and you set it to 0 so change it to false and it should work
public static readonly DependencyProperty ModifiedProperty =
DependencyProperty.Register("Modified",
typeof(bool),
typeof(Foo),
new UIPropertyMetadata(false));
Update
I'm not aware of any way to raise PropertyChanged
when binding directly to the DataContext. A little workaround you could use is to bind to a property called This which simply returns this
<Window.Title>
<Binding Path="This" Converter="{StaticResource windowTitleConverter}"/>
</Window.Title>
Then you can use the PropertyChangedCallback
to raise PropertyChanged
for This
public class Foo : DependencyObject, INotifyPropertyChanged
{
public Object This
{
get { return this; }
}
public bool Modified
{
get { return (bool)GetValue(ModifiedProperty); }
set { SetValue(ModifiedProperty, value); }
}
public string FileName
{
get { return (string)GetValue(FileNameProperty); }
set { SetValue(FileNameProperty, value); }
}
public static readonly DependencyProperty FileNameProperty =
DependencyProperty.Register("FileName",
typeof(string),
typeof(Foo),
new UIPropertyMetadata(string.Empty, new PropertyChangedCallback(OnFileNameChanged)));
public static readonly DependencyProperty ModifiedProperty =
DependencyProperty.Register("Modified",
typeof(bool),
typeof(Foo),
new UIPropertyMetadata(false, new PropertyChangedCallback(OnModifiedChanged)));
private static void OnFileNameChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e)
{
Foo foo = obj as Foo;
foo.OnPropertyChanged("This");
}
private static void OnModifiedChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e)
{
Foo foo = obj as Foo;
foo.OnPropertyChanged("This");
}
public event PropertyChangedEventHandler PropertyChanged;
public void OnPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
Another solution would be to use a MultiBinding instead which would remove the need for the This property
<Window.Resources>
<local:TitleMultiConverter x:Key="TitleMultiConverter"/>
</Window.Resources>
<Window.Title>
<MultiBinding Converter="{StaticResource TitleMultiConverter}">
<Binding Path="FileName"/>
<Binding Path="Modified"/>
</MultiBinding>
</Window.Title>
TitleMultiConverter
public class TitleMultiConverter : IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
string fileName = values[0].ToString();
bool modified = (bool)values[1];
if (fileName == null)
return "Foo";
return fileName + (modified ? " *" : "");
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
精彩评论