N.B.: THIS IS NOT JUST ABOUT THE CUSTOM MARKUP EXTENSIONS. PLEASE READ BEFORE MARKING AS DUPLICATE.
I have a WPF markup extension with a converter, and the two of them go as follows:
[ValueConversion(typeof(WindowState), typeof(object))]
internal class WindowStateToObjectConverter : IValueConverter {
public WindowStateToObjectConverter() { }
public WindowStateToObjectConverter(object maximized, object normal) {
this.maximized = maximized;
this.normal = normal;
}
#region Properties
#region Maximized Property
private object maximized;
public object Maximized {
get { return maximized; }
set { maximized = value; }
}
#endregion
#region Normal Property
private object normal;
public object Normal {
get { return normal; }
set { normal = value; }
}
#endregion
#endregion
public object Convert(object value, Type targetType, object param, CultureInfo culture) {
if((value as WindowState? ?? WindowState.Normal) == WindowState.Maximized) return maximized;
else return normal;
}
public object ConvertBack(object value, Type targetType, object param, CultureInfo culture) {
throw new InvalidOperationException("Cannot convert downwards to WindowState");
}
}
[MarkupExtensionReturnType(typeof(Binding))]
internal class WindowMaximizedSwitchExtension : MarkupExtension {
object maximized, normal;
public WindowMaximizedSwitchExtension(object maximized, object normal) {
this.maximized = maximized;
this.normal = normal;
}
public override object ProvideValue(IServiceProvider serviceProvider) {
Binding ret = new Binding("WindowState");
RelativeSource retRSource = new RelativeSource(RelativeSourceMode.FindAncestor);
retRSource.AncestorType = typeof(Window);
ret.RelativeSource = retRSource;
ret.Converter = new WindowStateToObjectConverter(maximized, normal);
return ret.ProvideValue(serviceProvider);
}
}
They are for a custom window I am designing - they will be used to switch certain values (border width, margins, etc.) when the window is maximized. However, at design-time, they always return null, which is a real pain, because then my window looks like this:
...when it's supposed to look like this:
(Ignore that the title and icon are missing in the first image, although, if you have a solution, feel free to answer - it's essentially the same problem.)
For obvious reasons, it would be extremely hard to design with this. The only major issue you see with the window preview is the places where I've used the extension to set Row
/ColumnDefinition
s - when it returns null, the Height
/Width
is set to 1*
. So, my question is whether there is a way to select a default value, perhaps instead of the binding (e.g. the non-maximized value), at design time.
Well, I feel like an idiot, but I found the solution fairly quickly:
In the ProvideValue
method of the expression, I added the following line:
ret.FallbackValue = normal;
Where normal
is the value to use when the window is not maximized.
ProvideValue
now looks like this:
public override object ProvideValue(IServiceProvider serviceProvider) {
Binding ret = new Binding("WindowState");
RelativeSource retRSource = new RelativeSource(RelativeSourceMode.FindAncestor);
retRSource.AncestorType = typeof(Window);
ret.RelativeSource = retRSource;
ret.Converter = new WindowStateToObjectConverter(maximized, normal);
ret.FallbackValue = normal;
return ret.ProvideValue(serviceProvider);
}
This returns the normal value during design-time.