WPF Binding Control Widths so they are always the same width
I would like to bind the width of a Window to the ActualWidth of a control on another window.
As I have acheived so far, it updates the width on only once on showing the window, but does not update as the source control changes width.
Window 1 with DockPanel control, which has the ActualWidth I wish to bind to (in practice there are multiple controls, but I've only put one DockPanel here for simplicity):
<Window x:Class="Testing1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:Testing1"
mc:Ignorable="d"
Title="WindowWithControl" Height="350" Width="525" Initialized="Window_Initialized" Loaded="Window_Loaded" Closed="Window_Closed">
<Grid Name="gridMain">
<Grid.RowDefinitions>
<RowDefinition />
</Grid.RowDefinitions>
<DockPanel Name="dockBindSource" Grid.Row="0" >
</DockPanel>
</Grid>
</Window>
Window 2 - This is the window I want to match the width of the control in the previous window.
<Window x:Class="Testing1.ProgressOverlay"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:Testing1"
mc:Ignorable="d"
Title="ProgressOverlay" Height="300" Width="300" ShowInTaskbar="False" Opacity="0.75" AllowsTransparency="True" WindowStyle="None">
<Canvas Name="canvasPB" />
</Window>
Code in cs to test binding the width here. I need to do this at runtime, beacuse at design time I do not know which control the window will be linked to.
private void Window_Loaded(object sender, RoutedEventArgs e)
{
ProgressOverlay po = new ProgressOverlay();
po.Owner = this;
Binding b1 = new Binding();
b1.Mode = BindingMode.OneWay;
b1.Source = this.dockBindSource.ActualWidth;
po.SetBinding(FrameworkElement.WidthProperty, b1);
po.Show();
}
All I've read about implementing binding is the requirement of INotifyPropertyChanged. I've used this before on custom classes, but not sure how to use on an existing control class.
I presume I need to raise a property change event of some sort in this part of the XML: <Canvas Name="canvasPB" />
Otherwise I'll give up on binding and just add a SizeChanged event to the Canvas, but throught there might be a cleaner way.
I don't know why, but it seems the binding on Window.Width is not working. Proposed workaround: set the window to SizeToContent="Width" and bind the width to the window content (canvasPB)
Binding b1 = new Binding("ActualWidth");
b1.Mode = BindingMode.OneWay;
b1.Source = this.dockBindSource;
po.canvasPB.SetBinding(FrameworkElement.WidthProperty, b1);
Try this:
private void Window_Loaded(object sender, RoutedEventArgs e)
{
ProgressOverlay po = new ProgressOverlay();
po.Owner = this;
Binding b1 = new Binding("ActualWidth");
b1.Mode = BindingMode.OneWay;
b1.Source = this.dockFilterClient;
po.SetBinding(FrameworkElement.WidthProperty, b1);
po.Show();
}
The Binding's Source property holds a reference to the source object of the binding, while its Path references the (optionally nested) source property.
The Binding constructor with a string argument sets the Path property, equivalent to
b1.Path = new PropertyPath("ActualWidth");
in your case.
Related
I am attempting to bind my window's "ShowInTaskbar" property to a definition in the code-behind, but it is not recognized. I also cannot programmatically assign the value in code-behind. I can only manipulate it by directly editing the xaml, which is not ideal in my situation.
<Window x:Class="WPFTest.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:WPFTest"
mc:Ignorable="d"
FontFamily="Segoe UI Light"
FontSize="14"
Title="WPF Test"
Name="Window"
Icon="/Resources/icon.ico"
Height="500"
Width="600"
WindowStartupLocation="CenterScreen"
ResizeMode="NoResize"
Loaded="Window_Loaded"
StateChanged="Window_StateChanged"
Closing="Window_Closing"
ShowInTaskbar="{Binding ShowInTaskbar}"
>
<Grid></Grid>
</Window>
The property I have in the code-behind:
private bool _showInTaskbar
public bool ShowInTaskbar
{
get
{
return _showInTaskbar;
}
set
{
_showInTaskbar = value;
OnPropertyChanged("ShowInTaskbar");
}
}
I have also tried:
Window.ShowInTaskbar = false;
Why is my binding not being recognized? Or, how can I simply define it in code-behind?
Ugh. It was because I was using the same name as the window naturally inherits. Once I changed my property to "ShowIconInTaskbar" I bound it and it functions properly.
If a ParentUserControl contains a TextBlock. The ParentUserControlalso contains ChildUserControl that has a TextBox. I want to set the TextBlock of ParentUserControl value from ChildTextBox. how can i ?
In other words somehow accessing the ParentUserControl and it's TextBlock element and then modifying it's value from ChildUserControl !
Update
i have a xaml window that contains a ParentUserControl that has a TextBlock. Now i am loading or adding another ChildUserControl into it on runtime. This newly added ChildUserControl contains a ChildTextBox. Now i want that when i input some value into this ChildTexBox the ParentUserControl's TextBlock should get that value and update itself.
Assuming we are not following any MVVM and a simple approach for this problem is,
Create a ChildUserControl with a textbox inside it as below,
<UserControl x:Class="SO52840402.ChildUserControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:SO52607887"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800">
<Grid>
<TextBox x:Name="ChildTextBox" />
</Grid> </UserControl>
Create a ParentUserControl which contains a TextBlock and ChildUserControl instance as shown below,
<UserControl x:Class="SO52840402.ParentUserControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:SO52607887"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800">
<Grid>
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>
<TextBlock x:Name="ParentTextBlock" Text="Hallo World!"/>
<local:ChildUserControl x:Name="ChildUserControl" Grid.Row="1" />
</Grid> </UserControl>
Now create a TextChanged event for the TextBox which is under ChildUserControl from code behind of ParentUserControl constructor after "InitializeComponent" as shown below,
public ParentUserControl()
{
InitializeComponent();
ChildUserControl.ChildTextBox.TextChanged += OnChildTextBox_TextChanged;
}
private void OnChildTextBox_TextChanged(object sender, EventArgs e)
{
ParentTextBlock.Text = (sender as TextBox).Text;
}
Note:- This is a not a recommended approach. For best approach, follow MVVM pattern and understand your requirements and do the design. Since you need something from a child user control from a parent user control, a best approach is to have a ViewModel bind to parent and child and access child view model in parent viewmodel and do "what ever you want".
The structure of my MainWindow.xaml is as bellow
<Window>
<Grid>
<TabControl Name="MainTabCntrl">
<TabItem1>
<TabItem2>
<TabItem3>
.
.
.
<TabItemN>
</TabControl>
</Grid>
</Window>
The problem is that my MainWindow.xaml is currently ~4000 lines of code,which is not efficient(do you agree?)
the solution I'm trying to implement is to create N windows (representing my TabItems) separately and each time user click on Tab items I load the related windows in that TabItem as shown bellow
private void inventory_start()//this function is called in my MainWinodw.xaml.cs
{
inv = new Inventory(db, logViewer);//this is a window
TabItem tbItem = new TabItem();
Frame frame = new Frame();
frame.Content = inv;
tbItem.Name = "invTab";
tbItem.Content = frame;
tbItem.IsSelected = true;
MainTabCntrl.Items.Add(tbItem);
inv.swithInventoryTabs("inv_info");
}
I have an error now , "'Management_V0.Inventory' root element is not valid for navigation."
A window cannot be a child of another element. Period.
But you could just move the contents of the Inventory window to a UserControl (by for example simply copy and paste the XAML and the code from one file to another) and use this one as the Content of the Inventory window and the Frame:
<Window x:Class="WpfApplication1.Inventory"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:WpfApplication1"
mc:Ignorable="d"
Title="Inventory" Height="300" Width="300">
<local:UserControl1 />
</Window>
Frame frame = new Frame();
frame.Content = new UserControl1();
best way to use tabControl is with User control: Example below:
XAML
<TabControl >
<TabItem x:Name="tab1" Header="UserControl"></TabItem>
<TabItem x:Name="tab2" Header="noControl"></TabItem>
</TabControl>
and in code behind class:
tab1.Content = new UserControl1();
and than Add new user control of name UserControl1:
<UserControl x:Class="WpfApplication1.UserControl1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300">
<Grid>
<Viewbox>
<TextBlock Text="User Control"/>
</Viewbox>
</Grid>
</UserControl>
I am trying to view the coordinates of a mouse click on a WPF screen with no luck. I have a rudimentary grid layout with a Textblock that will display these coordinates. I have bound values from the xaml to a code-behind before but am not sure if this direction is possible. My xaml is as follows
<Window x:Class="MouseUpExample.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"
Name="MyWindow">
<Grid>
<TextBlock Text="{Binding Path=GetMouseCoordinates}"/>
</Grid>
</Window>
and my code-behind is as follows
using System.Windows;
using System.Windows.Input;
using System.Windows.Shapes;
namespace MouseUpExample
{
public partial class MainWindow : Window
{
Point currentPoint = new Point();
public MainWindow()
{
InitializeComponent();
}
private string GetMouseCoordinates(object sender, System.Windows.Input.MouseButtonEventArgs e)
{
if (e.ButtonState == MouseButtonState.Pressed)
{
currentPoint = e.GetPosition(this);
return currentPoint.ToString();
}
return "error";
}
}
any help would be greatly appreciated, thank you.
You've got multiple problems here.
Problem #1 is that you can't bind to a method. It needs to be a property and preferably either a DependencyProperty or one that takes part in the INotifyPropertyChanged interface.
Problem #2 is that binding works with the DataContext by default, but you're not setting the DataContext of your TextBlock, either explicitly or implicitly.
Problem #3 is that this doesn't really make sense. Is GetMouseCoordinates an event handler for something? You probably would want to split out the event handler and the property.
I'd suggest that you go read up on DataBinding in WPF and then give it another shot.
To be able to bind you'd have to set the DataContext to a class that implements INotifyPropertyChanged and bind to its properties. As you don't have it defined I'd set that text directly in the view.
<Window x:Class="MouseUpExample.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"
Name="MyWindow" MouseDown="MainWindow_OnMouseDown">
<Grid>
<TextBlock Name="MyTextBlock"/>
</Grid>
</Window>
And in the code behind:
private void MainWindow_OnMouseDown(object sender, MouseButtonEventArgs e)
{
MyTextBlock.Text = e.GetPosition(this).ToString();
}
I have a window with a Grid on.
On this I have some buttons, one of which when clicked will create a new 'PostIt' which is a user control I have created.
What I want to do is click on a 'PostIt' and have that control on top of all the others.
I have tried...
Grid.SetZIndex(sender, value);
Which seems to be the correct code, no errors, just not movement of the control :(
The problem may lie in the fact that the code for the click is in the user control and not the mainwindow cs file. Does this matter?
The 'PostIt' is simply a border with a text box in it.
Are you calling Grid.SetZIndex(sender, value) in a handler of the PostIt mouse click, or a handler for a control inside the PostIt? What is the value that you are setting?
Here is an example that works:
<UserControl x:Class="WpfApplication1.UserControl1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300" MouseUp="UserControl_MouseUp">
<Grid>
</Grid>
</UserControl>
public partial class UserControl1 : UserControl
{
public UserControl1()
{
InitializeComponent();
}
private void UserControl_MouseUp(object sender, MouseButtonEventArgs e)
{
Panel.SetZIndex(this, Panel.GetZIndex(this) + 2);
}
}
<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="MainWindow" Height="350" Width="525">
<Grid>
<local:UserControl1 Background="Green" Margin="40,40,100,100" Panel.ZIndex="0" />
<local:UserControl1 Background="Red" Margin="140,140,10,10" Panel.ZIndex="1" />
</Grid>
</Window>
Jogy
This may not be the best solution, but it's the one that worked for me; I was re-ordering two grids:
GridOnBottom.SetValue(Grid.ZIndexProperty, (int)GridOnTop.GetValue(Grid.ZIndexProperty) + 1);
...with GridOnBottom and GridOnTop renamed to the instances of the objects you're re-ordering. Granted, it's not the best solution, but it works.