I am trying to bind ChildWindow Height property to my viewmodel property , but I think it only reads VM value on first load and doesn't change size when VM changes & notifies about the change. In debugger I see that it goes into Height getter once, further notifications don't change ChildWindow size..
I think it should be bindable so I am wondering whether some issue exists here or I am doing some mistake?
Sounds like one time binding, but its oneway..
Height="{Binding WindowHeight,Mode=OneWay}"
Further investigation shows that when we change binding to Mode=TwoWay and add an empty setter it begins to behave as expected. But that doesn't explain the reason why OneWay binding doesn't work. Also value that is passed to setter is equal to my whole application height, not just childwindow that is obviously supposed to be smaller.
The most strange thing with this whole situation is the following:
Also this value is passed to setter
4 consecutive times everytime after a getter is called (see
count++ that is used to count that).
It is fired before dialog is actually shown, and it always goes in sequence get,set,set,set,set
Code for view model is super simple. Nowhere in code anyone is using ChildWindow Height, its only set in its xaml binding as shown above.
private int count = 0;
public int WindowHeight
{
get { return IsDefaultMode? DEFAULT_HEIGHT : SPECIAL_HEIGHT; }
set {count++; }
}
My inheriting Childwindow class contains like 5 strings of text none of which affect Height in any way.
Notification about WindowHeight is not fired by WindowHeight property (as seen in code) , its fired by Mode property. Couldve been a converter around mode but its currently implemented this way cause I am not sure a special converter with a couple of magic values for this situation is a better approach.
oks . mode setter code:
public bool IsSpecialMode
{
get { return m_IsSpecialMode; }
set
{
if (m_IsSpecialMode!= value)
{
m_IsSpecialMode= value;
NotifyPropertyChanged("IsSpecialMode");
NotifyPropertyChanged("WindowHeight");
}
}
}
If the ChildWindow, or any other object, changes the Height property then your binding will be lost. Try setting it to a TwoWay binding and set a break point in your View-model's WindowHeight property's setter. That will tell you what is setting it and whether you can have a OneWay binding.
The ChildWindow class will actually set it's own Height and Width properties. For example, the following code ensures the ChildWindow always covers the root content of your application. This allows the ChildWindow to provide the overlay or faded effect when showing it's popup:
private void UpdateOverlaySize()
{
if (((this.Overlay != null) && (Application.Current != null)) && ((Application.Current.Host != null) && (Application.Current.Host.Content != null)))
{
base.Height = Application.Current.Host.Content.ActualHeight;
base.Width = Application.Current.Host.Content.ActualWidth;
// ... other code removed
}
}
So if effect, it looks like you can't use a OneWay binding on the Height or Width properties.
Related
I'm wanting to validate Textbox entries for an application and provide both visual and verbal feedback in the case of an error. I'd like the normal Textbox border behavior you get from wpf when using ValidationRules or INotifyDataError, then in a separate TextBlock display an error message. Standard stuff. I've got a few ways to approach this but each one is giving me problems.
ValidationRules seems like the easiest system-based approach. My problem here is binding the error message to the separate TextBlock. Most examples use content presenters or error templates, but is there no simple way to bind to a ValidationResult directly? (Question 1)
INotifyDataError being the more modern approach is nice in theory, but it seems wayyy too complicated for what I'm doing.
However, the simplest way I have found is validating directly in the property's setter in the Viewmodel. Something like this:
private string _fileName;
public string FileName
{
get {return _fileName;}
set
{
_fileName = value;
if(String.IsEmptyOrNull(value))
{
FileNameError = "File must have a name.";
}
else
{
FileNameError = null;
}
RaisePropertyChanged();
}
}
private string _fileNameError;
public string FileNameError
{
get {return _fileNameError;}
set
{
_fileNameError = value;
RaisePropertyChanged();
}
}
Then, to show an error, I can bind 2x to FileNameError: Once to display the text in the TextBlock, and the other through a converter (checking for !null) to show the red border around the TextBox.
The problem here the way WPF does the red border is much better; it appears to be its own element rather than an actual border. When I try my StringToBorderBrush converter, the border's thickness reduces the TextBox size, so things no longer line up. The double whammy is that by setting the BorderBrush to transparent, I lose the TextBox's default border and focus border which I'd like to keep.
So is there a way to either duplicate what WPF is doing with the border, or to somehow control it with a property? I would think this would be possible because something has to call it when it is shown with ValidationRules or INotifyDataError, right? (Question 2)
I have a StackPanel and an Image. When the user clicks a button, the StackPanel Visibility property is changing to Collapsed, making the image to change location.
Is there an event for that scenario? Another way to know when it is happening?
Presuming that you are using a binding to change the Visibility of the StackPanel you could change the setter of the binding source to call a method on change:
Visibility vis;
public Visibility Vis
{
get { return vis; }
set
{
vis = value;
imageLocationChanged();
NotifyPropertyChanged("Vis");
}
}
void imageLocationChanged()
{
//Do stuff
}
Note: If you have multiple bindings that may influence the images position, you would have to call this method from each setter
EDIT (reflecting OP's comment):
If setting the visibility in the code behind without binding then just call the method you need after setting the visibility:
stackpanel1.Visibility = Visibility.Collapsed;
imageLocationChanged();
I am trying to set my child window to the size of my application so it takes up the entire screen. I am using the following code:
Binding widthBinding = new Binding("Width");
widthBinding.Source = App.Current.Host.Content.ActualWidth;
this.SetBinding(ChildWindow.WidthProperty, widthBinding);
Binding heightBinding = new Binding("Height");
heightBinding.Source = App.Current.Host.Content.ActualHeight;
this.SetBinding(ChildWindow.HeightProperty, heightBinding);
Where this is the child window.
I am binding it so that when they resize their browser, the child window should as well. However, my child window isn't binding to the sizes. It still remains its default size. Are my binding incorrect?
I'm not confident you're going to get binding to work. The easiest method to make your ChildWindow fill the screen is just set the HorizontalAlignment & VerticalAlignment to Stretch
<controls:ChildWindow x:Class="SilverlightApplication4.ChildWindow1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:controls="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls"
Title="ChildWindow1"
HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
If you absolutely want to go the ActualWidth/ActualHeight route in silverlight, you'd have to do something like...
public ChildWindow1()
{
InitializeComponent();
UpdateSize( null, EventArgs.Empty );
App.Current.Host.Content.Resized += UpdateSize;
}
protected override void OnClosed( EventArgs e )
{
App.Current.Host.Content.Resized -= UpdateSize;
}
private void UpdateSize( object sender, EventArgs e )
{
this.Width = App.Current.Host.Content.ActualWidth;
this.Height = App.Current.Host.Content.ActualHeight;
this.UpdateLayout();
}
I think you're trying to bind to ActualWidth.Width, which doesn't exist. Remove the "Width"/"Height" strings from your binding constructor and it should work.
Binding widthBinding = new Binding();
widthBinding.Source = App.Current.Host.Content.ActualWidth;
this.SetBinding(ChildWindow.WidthProperty, widthBinding);
Binding heightBinding = new Binding();
heightBinding.Source = App.Current.Host.Content.ActualHeight;
this.SetBinding(ChildWindow.HeightProperty, heightBinding);
The Content class does not raise a PropertyChanged event when ActualHeight and ActualWidth change; so the Binding has no way of knowing that it needs to refresh the values. There are some complicated ways that you could get around this while still using Binding, but the simplest answer will just be to handle the Content.Resized event and set the values yourself.
If #Rachel's answer doesn't work, you might want to try the technique outlined in this blog posting:
http://meleak.wordpress.com/2011/08/28/onewaytosource-binding-for-readonly-dependency-property/
According to that post, you cannot bind to readonly properties, which ActualWidth and ActualHeight are.
I don't know if this will work in Silverlight, but it has worked well for us in WPF.
ActualWidth and ActualHeight do not fire PropertyChanged events in Silverlight. This is by design (something about optimizing the performance of the layout engine, if I recall). Thus you should never try to bind to them because it simply won't work. The recommended solution is to handle the SizeChanged event and then update things appropriately yourself. From the documentation:
Do not attempt to use ActualWidth as a binding source for an
ElementName binding. If you have a scenario that requires updates
based on ActualWidth, use a SizeChanged handler.
Here's a solution that uses attached properties. It should also be straight forward to wrap this functionality in a XAML friendly Blend behavior (probably Behavior<FrameworkElement>).
Let's say I create a custom control which embed a trackbar. I also create an orientation property for my custom control.
When I drop the custom control on a form by default it will be horizontal. Then I set it to vertical, the trackbar should refresh to be vertical at design time.
How to do so ?
I think you should call Refresh() after changing the value:
public OrientationProperty Direction
{
get
{
return _direction;
}
set
{
_direction = value;
if (DesignMode)
{
Parent.Refresh(); // Refreshes the client area of the parent control
}
}
}
private OrientationProperty _direction;
Here's my solution to this issue:
1. Whenever you set something property, call Invalidate() in the setter.
2. After correspondent properties and refreshing method (for eg. overridden OnPaint) are implemented, rebuild!!! then you'll see the modifications taken effect in design time
3. During design, always check whether compilation errors are present, as this might stop VS performing all his tasks.
With this, when I put my control on a form, and adjust its own properties, refreshing happens immediately as expected.
PS.: old post, but at least verified the behavior in VS2015 too :)
I have a simple Converter that adds a "+" symbol to a positive number that is entered in a TextBox. When the number is entered I want to initiate some action, but I don't want to wait until the TextBox loses focus: I want to update the binding immediately as the user enters the text.
The default behaviour of a TextBox is that when a user navigates away from the box, the binding source is updated (UpdateSourceTrigger=LostFocus). In this scenario, my converter works as expected, and the + is added. However, when I change it to the following, the + is never added.
<TextBox Text="{Binding Value, Converter={StaticResource PlusConverter}, UpdateSourceTrigger=PropertyChanged}" />
I can imagine that there is a good reason why my converter.Convert() method is not called. I would like to have the following behaviour:
When the users enters text, the source is updated immediately
When the TextBox loses focus, the + is added.
Now I'm looking for a nice, but especially GENERIC way of doing this (as I have a lot of these TextBoxes in my app).
So far I haven't been able to come up with a proper solution.
Agree w/Kent B, you need to post your Converter code.
I've been able to get part 1 to work with a simple converter (I'm binding a second unconverted TextBlock to show that the value is indeed getting updated).
However, if I understand your part 2, you're trying to get the TextBox's text to update with a "+" after it loses focus. This is a little trickier and I don't think you'll be able to do it with just an IConverter. If it can be done, I'd love to know the answer as well.
What you're essentially asking for is watermarked input behavior e.g. allow a user to enter some data, and have it get formatted correctly (both in the underlying DataBinding and in the UI). The quickest/dirtiest solution to this is to handle the TextBoxes' LostFocus but since you're using that all over your app, this may not be feasible.
You could also consider wrapping the TextBox in your own UserControl. If you look at WPFToolkit's implementation of a DatePicker it has similar behavior: allow the user to enter free form text, then auto-convert the value to a DateTime (if valid) and show the DateTime in a localized format.
HTH.
The other thing you might want to do, is edit the template for TextBox and move the actual PART_ContentHost to the right a bit, then have a TextBlock indicate the +/- part; i.e. change the template of the TextBox from:
Control
- Border
-- PART_ContentHost (the actual editing part)
into:
Control
- Border
-- Horizontal StackPanel
--- TextBlock (contains +/- sign, has 2px right margin)
--- PART_ContentHost (actual editable section)
Then, bind the TextBlock's content to the text, but with a converter that either writes a '+' or '-'. This way, the user can't delete the +/- part, and you don't have to worry about parsing it; this also makes it easier if you want to do something like make the negative sign red or something.
Thanks for your answers! I looked into this issue myself a bit futher and came up with the following solution (which I'm not entirely satisfied with, but it works fine)
I've created a CustomControl that adds functionality to the TextBox.
It provided an event handler for the LostFocus event
When this event occurs, my converter is called.
However, the way I resolve the Converter is not very satisfying (I take it from the Style that is associated with my TextBox). The Style has a Setter for the Text property. From that setter I can access my Converter.
Of course I could also make a "PlusTextBox", but I use different converters and I wanted a generic solution.
public class TextBoxEx : TextBox
{
public TextBoxEx()
{
AddHandler(LostFocusEvent,
new RoutedEventHandler(CallConverter), true);
}
public Type SourceType
{
get { return (Type)GetValue(SourceTypeProperty); }
set { SetValue(SourceTypeProperty, value); }
}
// Using a DependencyProperty as the backing store for SourceType. This enables animation, styling, binding, etc...
public static readonly DependencyProperty SourceTypeProperty =
DependencyProperty.Register("SourceType", typeof(Type), typeof(TextBoxEx), new UIPropertyMetadata(null));
private static void CallConverter(object sender, RoutedEventArgs e)
{
TextBoxEx textBoxEx = sender as TextBoxEx;
if (textBoxEx.Style == null) {
return;
}
if (textBoxEx.SourceType == null) {
}
foreach (Setter setter in textBoxEx.Style.Setters) {
if (setter.Property.ToString() == "Text") {
if (! (setter.Value is Binding) ) {
return;
}
Binding binding = setter.Value as Binding;
if (binding.Converter == null) {
return;
}
object value = binding.Converter.ConvertBack(textBoxEx.Text, textBoxEx.SourceType, binding.ConverterParameter, System.Globalization.CultureInfo.CurrentCulture);
value = binding.Converter.Convert(value, typeof(string), binding.ConverterParameter, System.Globalization.CultureInfo.CurrentCulture);
if (!(value is string)) {
return;
}
textBoxEx.Text = (string)value;
}
}
}
}
In your converter, couldn't you just check the keyboard's current focus? Something like:
TextBox focusedTextBox = Keyboard.FocusedElement as TextBox;
if (focusedTextBox == null || focusedTextBox != senderOrWhatever)
{
' this is where you'd add the +, because the textbox doesn't have focus.
}