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>).
Related
I have a TabControl where I want to keep the tabs to a fixed size and I want icons in the tabs. I have set TabControl.SizeMode = Fixed and TabControl.ItemSize = 100, 18. I have also set TabControl.ImageList and am assigning images to the tabs via TabPage.ImageKey.
Here is what it looks like if I comment-out assigning the ImageKey:
And here is what it looks like if I am assigning the ImageKey:
Is there some sort of "alignment" for the icons? I want them to be on the far left in the blank space, but instead they are starting where the text starts. Any suggestions?
(BTW - if I set TabControl.SizeMode = Normal, I get the tab content the way I want it, but the tabs aren't a fixed size):
I can verify the issue that you are seeing with TabControl.SizeMode = Fixed (on Windows 10). I initially seen it in the designer when configuring a TabPage with an icon. However the irritating thing is that the issue corrected itself if the designer is closed and reopened. This suggests a window style setting of some sort and there are some Tab Control Styles set in the CreateParams Property based on the SizeMode Property. However, I found no solution in attempting to apply the TCS_FORCEICONLEFT style. If the ImageIndex property is set prior to the control being shown, then the alignment is as desired. So I figured that there must be something being configured on handle creation.
If you call the form's RecreateHandle method after setting the TabPage.ImageIndex property, the form redraws and all looks good. However this cause the form to blink. Calling the Control.RecreateHandle method on the TabControl also works. This is a protected method and would necessitate using a derived TabControl to expose the method or you could use Reflection to invoke the method.
public class MyTC : TabControl
{
public void FixIcon()
{
RecreateHandle();
}
}
I have a user control written in C# & WPF using the MVVM pattern.
All I want to do is have a property in the bound ViewModel exposed to outside of the control. I want to be able to bind to it and I want any changes to the property to be picked up by anything outside the control that is bound to the exposed value.
This sounds simple, but its making me pull out my hair (and there is not much of that left).
I have a dependency property in the user control. The ViewModel has the property implementing the INotifyPropertyChanged interface and is calling the PropertyChanged event correctly.
Some questions:
1) How do I pick up the changes to the ViewModel Property and tie it to the Dependency Property without breaking the MVVM separation? So far the only way I've managed to do this is to assign the ViewModels PropertyChanged Event in the Controls code behind, which is definitely not MVVM.
2) Using the above fudge, I can get the Dependency property to kick off its PropertyChangedCallback, but anything bound to it outside the control does not pick up the change.
There has to be a simple way to do all of this. Note that I've not posted any code here - I'm hoping not to influence the answers with my existing code. Also, you'd probably all laugh at it anyway...
Rob
OK, to clarify - code examples:
usercontrol code behind:
public static DependencyProperty NewRepositoryRunProperty = DependencyProperty.Register("NewRepositoryRun", typeof(int?), typeof(GroupTree),
new FrameworkPropertyMetadata( null, new PropertyChangedCallback(OnNewRepositoryRunChanged)));
public int? NewRepositoryRun
{
get { return (int?)GetValue(NewRepositoryRunProperty); }
set
{
SetValue(NewRepositoryRunProperty, value);
}
}
private static void OnNewRepositoryRunChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
if (e.OldValue != e.NewValue)
{
}
}
public GroupTree()
{
InitializeComponent();
GroupTreeVM vm = new GroupTreeVM();
this.DataContext = vm;
}
Viewmodel (GroupTreeVM.cs)
private int? _NewRepositoryRun;
public int? NewRepositoryRun
{
get
{
return _NewRepositoryRun;
}
set
{
_NewRepositoryRun = value;
NotifyPropertyChanged();
}
}
And now for my weekly "don't do that" answer...
Creating a ViewModel for your UserControl is a code smell.
You're experiencing this issue because of that smell, and it should be an indication that you're doing something wrong.
The solution is to ditch the VM built for the UserControl. If it contains business logic, it should be moved to an appropriate location in another ViewModel.
You should think of a UserControl as nothing more than a more complex control. Does the TextBox have its own ViewModel? No. You bind your VM's property to the Text property of the control, and the control shows your text in its UI.
Think of UserControls in MVVM like this--For each model, you have a UserControl, and it is designed to present the data in that model to the user. You can use it anywhere you want to show the user that model. Does it need a button? Expose an ICommand property on your UserControl and let your business logic bind to it. Does your business logic need to know something going on inside? Add a routed event.
Normally, in WPF, if you find yourself asking why it hurts to do something, it's because you shouldn't do it.
Perhaps I've misunderstood, but it seems like you're trying to use binding in the code behind?
public MyUserControl()
{
InitializeComponent();
// Set your datacontext.
var binding = new Binding("SomeVMProperty");
binding.Source = this.DataContext;
SetBinding(MyDependencyProperty, binding);
}
All menus/contextmenus/toolbars I use in wpf are declared in ViewModel code pretty much like this:
MenuService.Add( new MenuItem()
{
Header = "DoStuff",
Command = new relayCommand( DoStuff, () => CanDoStuffExecute() )
// some more properties like parent item/image/...
} );
The MenuService provides a single binding point which is a hierarchical list of MenuItem and gets bound to the actual Menu's ItemsSource in xaml.
This works very well and now I'd like to add keyboard shortcuts in the same convenient way.
Ideally MenuItem would get a property of type System.Windows.Input.KeyGesture so I can simply write
Shortcut = new KeyGesture( Key.D, ModifierKeys.Control )
which would result in the Command of the item being called upon hitting Ctrl+D in the window that owns the menu, and which would also lead to automatically display "Ctrl+D" in the menu.
However I'm lost here: I wanted to set the MenuItem.InputBindings collection via databinding but it is get-only. How can I get items into it anyway? Or is there an MVVM framework that already supports something like this? Most q&a I found on keyboard shortcuts are all about setting the shortcuts through xaml, which is of no help.
Update
Searching for 'relaycommand vs routeduicommand and 'relaycommand keygesture' etc did reveal enough information to come up with a working though hacky solution. There are definitely other and better ways out there, but at the moment this is ultra low priority for me and does the job perfectly. I added two properties to the MenuItem class like this:
//Upon setting a Gesture, the original command is replaced with a RoutedCommand
//since that automatically gives us proper display of the keyboard shortcut.
//The RoutedCommand simply calls back into the original Command.
//It also sets the CommandBinding property, which still has to be added to the
//CommandBindingCollection of either the bound control or one of it ancestors
public InputGesture Gesture
{
set
{
var origCmd = Command;
if( origCmd == null )
return;
var routedCmd = new RoutedCommand( Header,
typeof( System.Windows.Controls.MenuItem ),
new InputGestureCollection { value } );
CommandBinding = new CommandBinding( routedCmd,
( sender, args ) => origCmd.Execute( args.Parameter ),
( sender, args ) => { args.CanExecute = origCmd.CanExecute( args.Parameter ); } );
Command = routedCmd;
}
}
//CommandBinding created when setting Gesture
public CommandBinding CommandBinding { get; private set; }
So this gives the functionality I asked for originally (ie adding keyboard shortcuts in code where they are easily configurable etc). All that is left is to register the commandbindings. At the moment this is done simply by adding all of them to Application.Current.MainWindow.CommandBindings.
This doesn't actually qualify as an 'answer' (I'm not able to add a comment evidently) - but I'd suggest that what you're doing, is not the intended method in WPF. You're doing this the Windows Forms way (and as in many other toolkits) - defining your UX in code. You got as far as you did, but now you've run into a brick wall: the key gestures are purely UX, definitely not to be specified in code-behind. The appearance (as a function of the view-model), and the user's interaction with it (ways of making a given command happen) are for the XAML definition.
Property values, and Commands are for your view-model, so that you can reuse this view-model for other views, and also easily create unit-tests for it. How would implementing your keyboard shortcuts in the view-model help the testability? And for use in other views, one could argue that the actual shortcuts might not apply to a new view, so that is not where those belong. You may have other reasons of course - but I'd suggest you might consider just defining these in XAML.
-Added, in response to your comment-
You're quite right - and I've seen some rather large WPF UX projects that tried hard to avoid any code-and wound up unnecessarily obtuse. I try to just use whichever approach yields a working result, and is as simple as I can get it.
Here is a sample snippet that simply creates the MenuItem..
<Menu x:Name="miMain" DockPanel.Dock="Top">
<MenuItem Command="{Binding Path=MyGreatCommand}" Header="DoSomething"/>
That creates the menu. Here, MyGreatCommand is an ICommand, and is simply a property on the view-model. I generally place that within a DockPanel, to handle the StatusBar, etc.
To assign the key gesture..
<Window.InputBindings>
<KeyBinding Key="X" Modifiers="ALT" Command="{Binding Path=MyGreatCommand}"/>
However, since you mentioned that you've already searched for answers and found only XAML - I assume you've already tried this approach. I have used RoutedUICommands instead of user-defined ICommands, to get that nice right-aligned key-gesture in the header text, but I haven't found how to do both. If you insist upon creating the commands and key-gestures all in code, you may have to create RoutedUICommands.
Why are you wanting to set the key-gestures in other than your XAML?
If you want some menu-items to appear only when certain states hold sway within your view-model, then you can bind the Visibility property of a menu-item (which can contain other menu-items) to Collapsed or Visible.
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.
In WPF 4.0, I can't seem to get any keyboard shortcuts to work if I swap the user control in the window after it's been loaded. A code sample says a thousand words, so here's what I'm doing:
Window window = new Window { Width = 800, Height = 600 };
window.Loaded += delegate
{
editor = new EditorRoot();
window.Content = editor;
};
app.Run(window);
window gets KeyDown events (and routed commands work fine), but editor never gets any keyboard events (nor do any controls within it). I tried:
editor.Loaded += (sender, e) => Keyboard.Focus(editor);
... but that didn't do anything. EditorRoot extends UserControl and has IsFocusable=true Any ideas what's wrong?
And if this does not work - use Dispatcher.BeginInvoke. From my experience - setting focus synchronously doesn't always work. And not only in WPF 4.
Maybe you could try the FocusManager instead of your approach. I use it and it works, you can even use it in XAML:
FocusManager.FocusedElement=editor;