In WPF How to prevent Controls inside ScrollViewer from expanding - c#

I'm trying to achieve something that sounds pretty simple, in WPF, but just can't get around doing it.
I have a ScrollViewer which contains two GroupBoxes. First one has it's height set to a fixed value, second one would take what's left of the window but have a MinHeight. Each GroupBox contains a DataGrid.
What i'm trying to do is :
The second groupbox should be sized to what's left of the Window and the DataGrid inside of it should be sized to fill the group box and have it's own scrollbar if rows can't all be shown. A scrollbar should appear in the window if i resize the window to be less than GroupBox1.Height+GroupBox2.MinHeight.
The behavior i get now is, the DataGrid in the second groupbox's height grows with the number of lines, thus expanding the Groupbox and having the Scrollviewer's scrollbar show up.
I came up with a little demo-app to show this behavior
WPF:
<Window x:Class="WpfApp1.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:WpfApp1"
mc:Ignorable="d"
Title="MainWindow"
Height="400"
Width="500">
<Grid>
<ScrollViewer VerticalScrollBarVisibility="Auto">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="150" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<GroupBox Header="test1"
Grid.Row="0">
<DataGrid ItemsSource="{Binding Colors}">
</DataGrid>
</GroupBox>
<GroupBox Header="test2"
Grid.Row="1"
MinHeight="50">
<DataGrid ItemsSource="{Binding Colors}">
</DataGrid>
</GroupBox>
</Grid>
</ScrollViewer>
</Grid>
C#
namespace WpfApp1
{
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
this.DataContext = this;
Colors = new List<Color>();
for (int i = 1; i < 51; i++)
{
byte b = (byte)(i * 5);
Colors.Add(Color.FromRgb(b,b,b));
}
}
private List<Color> _colors;
public List<Color> Colors
{
get
{
return _colors;
}
set
{
_colors = value;
}
}
}
}
What i'm getting :
What i would want (sorry for the bad photo-manipulation skills) :
Unless, as specified earlier, i resize the window to be smaller than the sum of the group1's fixed size and group2's min size, in which case i want the window's scrollbar.
In which case, i would want it to look like this : (again a mock-up, not an actual screenshot)
Mind you, the example is pretty simple but the window i'm trying to do it in is much more complex and it makes more sense to have a vertical scrollbar than it does in this example.
Thanks!

You can simply bind the MaxHeight property of the second GroupBox to the ActualHeight of the container of the ScrollViewer minus the first GroupBox.
Complete example (excluding the code behind which is same as yours.):
<Window.Resources>
<wpfApp1:SubtractConverter x:Key="SubtractConverter"/>
</Window.Resources>
<Grid Name="Root">
<ScrollViewer VerticalScrollBarVisibility="Auto">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="150" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<GroupBox
Name="Test1"
Header="test1"
Grid.Row="0">
<DataGrid ItemsSource="{Binding Colors}"/>
</GroupBox>
<GroupBox
Header="test2"
Grid.Row="1"
MinHeight="250">
<DataGrid ItemsSource="{Binding Colors}"/>
<GroupBox.MaxHeight>
<MultiBinding Converter="{StaticResource SubtractConverter}">
<Binding Path="ActualHeight" ElementName="Root"/>
<Binding Path="ActualHeight" ElementName="Test1"/>
</MultiBinding>
</GroupBox.MaxHeight>
</GroupBox>
</Grid>
</ScrollViewer>
</Grid>
public class SubtractConverter : IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
double[] doubles = values.Cast<double>().ToArray();
double result = doubles[0];
for (int i = 1; i < doubles.Length; i++)
{
result -= doubles[i];
}
return result;
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}

I don't know if this would be the easiest solution for your problem but you could do something along this line:
<Window x:Class="WpfApp1.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:WpfApp1"
mc:Ignorable="d"
Title="MainWindow"
Height="400"
Width="500">
<Window.Resources>
<local:HeightConverter x:Key="HeightConverter"/>
</Window.Resources>
<Grid>
<ScrollViewer VerticalScrollBarVisibility="Auto" x:Name="MainView">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="150" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<GroupBox Header="test1"
Grid.Row="0">
<DataGrid ItemsSource="{Binding Colors}">
</DataGrid>
</GroupBox>
<GroupBox Header="test2"
Grid.Row="1"
x:Name="grpBox2"
MinHeight="50">
<GroupBox.Height>
<MultiBinding Converter="{StaticResource HeightConverter}" ConverterParameter="150">
<Binding Path="ActualHeight" ElementName="MainView" />
<Binding Path="MinHeight" ElementName="grpBox2" />
</MultiBinding>
</GroupBox.Height>
<DataGrid ItemsSource="{Binding Colors}">
</DataGrid>
</GroupBox>
</Grid>
</ScrollViewer>
</Grid>
And for the converter something like this:
public class HeightConverter : IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
if (values == null || parameter == null || values[0] == null || values[1] == null)
{
return null;
}
var currentWindowHeight = double.Parse(values[0].ToString());
var currentMinHeight = double.Parse(values[1].ToString());
var currentTopWindowHeight = double.Parse(parameter.ToString());
var newHeight = currentWindowHeight - currentTopWindowHeight;
if (newHeight < currentMinHeight)
newHeight = currentMinHeight;
return newHeight;
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}

Related

How to dock listbox to a textbox overlapping other items

I am trying to make textbox with autocomplete drop-down listbox.
The problem lies in that there is not space for listbox, as there are other items just below the textbox. Although, not seen on screenshot, the space between textbox and button will be filled with table.
Is there a way to dock or align a listbox to the bottom of given textbox, regardless to other items in layout?
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="70"/>
<RowDefinition Height="*"/>
<RowDefinition Height="70"/>
</Grid.RowDefinitions>
<TextBox Grid.Row="0" x:Name="txtb_name"></TextBox>
<Grid Grid.Row="2" VerticalAlignment="Bottom">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Button Grid.Column="0" x:Name="btn_givecancel" Content="Cancel" Height="70" FontSize="18.667" Click="btn_givecancel_Click"/>
<Button Grid.Column="1" x:Name="btn_giveaccept" Content="Accept" Height="70" FontSize="18.667" Click="btn_giveaccept_Click"/>
</Grid>
</Grid>
Here's My Implementation:
Have a Converter that takes Total possible auto suggest item count and current view count:
<local:IntToVisibilityConverter x:Key="IntToVisibilityConverter"/>
Then here's is the Form with TextBox, ListBox and the Button controls.
<Grid Margin="50">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<TextBox Grid.Row="0" Text="{Binding UserText, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" DockPanel.Dock="Top"/>
<ListBox Grid.Row="1" Grid.RowSpan="2" VerticalAlignment="Top" MaxHeight="55" ItemsSource="{Binding SuggestionsFiltered, UpdateSourceTrigger=PropertyChanged}"
Canvas.ZIndex="1">
<ListBox.Visibility>
<MultiBinding Converter="{StaticResource IntToVisibilityConverter}">
<Binding Path="MaxCount"/>
<Binding Path="SuggestionsFiltered.Count"/>
</MultiBinding>
</ListBox.Visibility>
</ListBox>
<StackPanel Grid.Row="2" Orientation="Horizontal" Margin="0 50 0 0">
<Button Height="20" Width="100">Clear</Button>
<Button Height="20" Width="100" Margin="10 0 0 0">Accept</Button>
</StackPanel>
</Grid>
Finally, here's my DataContext:
public class TheDataContext
{
public TheDataContext()
{
FillData();
_SuggestionsFiltered = CollectionViewSource.GetDefaultView(_SuggestionSource);
_SuggestionsFiltered.Filter = obj =>
{
var opt = obj as string;
if (string.IsNullOrEmpty(_UserText) || _UserText.Length == 0)
return true;
return string.Join("", opt.Take(_UserText.Length)) == _UserText;
};
}
private void FillData()
{
_SuggestionSource = new List<string>();
_SuggestionSource.Add("Alpha");
_SuggestionSource.Add("Alpines");
_SuggestionSource.Add("Bravo");
_SuggestionSource.Add("Brood");
_SuggestionSource.Add("Charlie");
_SuggestionSource.Add("Charles");
_SuggestionSource.Add("Charlotte");
}
private string _UserText;
public string UserText
{
get => _UserText;
set
{
_UserText = value;
_SuggestionsFiltered.Refresh();
}
}
private List<string> _SuggestionSource;
public int MaxCount => _SuggestionSource.Count;
private ICollectionView _SuggestionsFiltered;
public ICollectionView SuggestionsFiltered
{
get => _SuggestionsFiltered;
}
}
Notice all the code around ICollectionView. Also, I have forcefully set some margins in xaml to show case that Listbox is drawn over other controls (courtsey ZIndex).
If you take this code, remember to handle selected event on ListBox, then set the textBox Text to this value. Also hide the listbox. Bit of a jugglery there.
Lastly, here is the Converter, if you are interested:
public class IntToVisibilityConverter : IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
int maxCount = System.Convert.ToInt32(values[0]);
int count = System.Convert.ToInt32(values[1]);
if (count > 0 && count != maxCount)
return Visibility.Visible;
return Visibility.Collapsed;
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}

Binding to ratio of heights

I'm trying to create a grid with two panes with a grid splitter between them that the user can reposition at runtime. I'd also like to bind a variable representing the ratio of the two panes. I've tried the following XAML:
<local:CustomConverter x:Key="splitPositionConverter2"/>
<StackPanel Margin="10">
<TextBox Name="txtValue" />
<Grid x:Name="splitViewGrid" Height="400">
<Grid.RowDefinitions>
<!--The top panel height is bound to the SplitPosition property. -->
<RowDefinition Height="{Binding ElementName=txtValue, Path=Text, Mode=TwoWay, Converter={StaticResource splitPositionConverter2}}"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Grid Grid.Row="0" Background="Red"/>
<GridSplitter Grid.Row="1"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
ResizeDirection="Rows"
Height="15"/>
<Grid Grid.Row="2" Background="Blue"/>
</Grid>
</StackPanel>
And I have the following converter:
public class CustomConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
Double db = 0.0;
if (Double.TryParse((string)value, out db))
return new GridLength(db, GridUnitType.Star); // value starts off as 2
else
return new GridLength(1.0, GridUnitType.Star);
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
if (value is GridLength)
{
GridLength GLen = (GridLength)value;
return GLen.Value.ToString(); // is a very large number
}
return 0;
}
}
So a value of 2 should have the red block to be twice the height of the blue block. The conversion from data to height seems to work but the ConvertBack gives me some weird values. A small nudge will give me a value in ConvertBack of 260* or so.
Am I interpreting this value incorrectly or is there an easier way of doing this?

Undesired button resizing after being transformed

I'm trying to create an expandable/collapsible menu for a personal project of mine. I have everything almost where I want it (in terms of it being behaving as expected anyway). When I collapse my menu, I want the buttons to rotate to a vertical position and not resize (Or at least resize to something that still fits the text). At the moment, the buttons rotate, then shrink vertically (what was/is the width) along with the parent control, which cuts off much of the contents. I can see why this would happen, but I can't think of a way around it that seems right to me.
Here is the behavior I'm seeing:
Before: After:
As you can see, the buttons are shrinking along their now-vertical width (to what I assume would be the width of the enclosing StackPanel).
Here is the code I am using:
ExpaningMenu.xaml
<UserControl x:Class="Budgety.Controls.ExpandingMenu"
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:Budgety.Controls"
mc:Ignorable="d"
Name="MainExpandingMenu"
MinWidth="32"
d:DesignHeight="300" d:DesignWidth="100">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition />
</Grid.RowDefinitions>
<StackPanel Name="MenuPanel" Width="100" HorizontalAlignment="Left" Background="{DynamicResource BackColor}" Grid.Row="1">
<!--Contents will go here-->
</StackPanel>
<Button Name="StateToggle" Width="100" Height="32" FontSize="18" VerticalAlignment="Center" HorizontalAlignment="Stretch" Panel.ZIndex="1" Background="{DynamicResource BackColor}" BorderThickness="0" Click="Button_Click" Content="«"></Button>
</Grid>
</UserControl>
ExpandingMenu.xaml.cs
public partial class ExpandingMenu : UserControl
{
public ExpandingMenu()
{
InitializeComponent();
//For testing purposes.
MenuPanel.Children.Add(new ExpandingMenuButton("TEST ITEM 1"));
MenuPanel.Children.Add(new ExpandingMenuButton("TEST ITEM 2"));
MenuPanel.Children.Add(new ExpandingMenuButton("TEST ITEM 3"));
MenuPanel.Children.Add(new ExpandingMenuButton("TEST ITEM 4"));
MenuPanel.Children.Add(new ExpandingMenuButton("TEST ITEM 5xxx"));
foreach (UIElement element in MenuPanel.Children)
{
(element as ExpandingMenuButton).HorizontalAlignment = HorizontalAlignment.Left;
}
}
#region Events
private void Button_Click(object sender, RoutedEventArgs e)
{
if (MenuPanel.Width == 100) //Need to collapse
{
StateToggle.Width = MenuPanel.Width = 32;
(sender as Button).Content = "\u00BB";
//Flip all children of this control (so far, assuming only ExpandingMenuButtons)
foreach (UIElement element in MenuPanel.Children)
{
(element as ExpandingMenuButton).LayoutTransform = new RotateTransform(-90);
//This works to resize to 100 tall (not ideal...)
//(element as ExpandingMenuButton).Width = 100;
//This does not seem to size to auto, which SHOULD make each button as long as the text requires... (this behavior is far less than ideal...)
//(element as ExpandingMenuButton).Width = Double.NaN;
}
}
else //Need to expand
{
StateToggle.Width = MenuPanel.Width = 100;
(sender as Button).Content = "\u00AB";
//Flip all children of this control (so far, assuming only ExpandingMenuButtons)
foreach (UIElement element in MenuPanel.Children)
{
(element as ExpandingMenuButton).LayoutTransform = new RotateTransform(0);
}
}
}
#endregion
}
ExpandingMenuButton.xaml
<UserControl x:Class="Budgety.Controls.ExpandingMenuButton"
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:Budgety.Controls"
mc:Ignorable="d"
d:DesignHeight="30" d:DesignWidth="100"
Height="30"
Name="ButtonControl">
<Grid Name="ButtonGrid" Height="30">
<ToggleButton Name="MenuButton" Background="Aqua" BorderThickness="1" Content="TEST"></ToggleButton>
</Grid>
</UserControl>
ExpandingMenuButton.xaml.cs
public partial class ExpandingMenuButton : UserControl
{
//Will definitely want custom functionalty here. TBD. Nothing special so far.
#region Constructors
public ExpandingMenuButton()
{
InitializeComponent();
}
public ExpandingMenuButton(string sText)
{
InitializeComponent();
MenuButton.Content = sText;
}
#endregion
}
If you'd like to test the code out, it should work placed in a normal grid as I have (The above mentioned UserControls I've made are in a Controls folder within the project):
<Window
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:Budgety"
xmlns:Controls="clr-namespace:Budgety.Controls" x:Class="Budgety.MainTest"
mc:Ignorable="d"
Title="MainTest" Height="600" Width="800">
<Grid>
<Controls:ExpandingMenu x:Name="ExpandingMenu" HorizontalAlignment="Left"/>
</Grid>
</Window>
After all is said and done, here is the behavior/look I am after (notice buttons are not shortened)
The reason for the layout you're seeing is the fixed height constraint you placed in ExpandingMenuButton: Height="30" on both the UserControl and the Grid element. You can change it to MinHeight.
In addition, when you set the width of the MenuPanel, you're also containing the height of the buttons, because you apply a transform.
Here's one way to fix this:
private void Button_Click(object sender, RoutedEventArgs e)
{
if (StateToggle.IsChecked == true)
{
StateToggle.Content = "\u00BB";
foreach (FrameworkElement element in MenuPanel.Children)
element.LayoutTransform = new RotateTransform(-90);
}
else
{
StateToggle.Content = "\u00AB";
foreach (FrameworkElement element in MenuPanel.Children)
element.LayoutTransform = null;
}
}
XAML:
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition />
</Grid.RowDefinitions>
<StackPanel Name="MenuPanel"
HorizontalAlignment="Left"
Background="{DynamicResource BackColor}"
Grid.Row="1">
<!--Contents will go here-->
</StackPanel>
<ToggleButton Name="StateToggle"
FontSize="18"
VerticalAlignment="Center"
HorizontalAlignment="Stretch"
Panel.ZIndex="1"
Background="{DynamicResource BackColor}"
BorderThickness="0"
Click="Button_Click"
Content="«" />
</Grid>
As a general rule, don't specify widths and heights in WPF - let the layout system do the measuring for you according to the content.

Winrt Wpf databinding

I am having no luck finding the issue of data binding not working. I have two user controls. The user control that uses the obervablecollection works fine. The user control bound to an object doesnt. If i assign the value to the text the value does appear. During debugging I can verify that the values are correct.
This logic is following Paul Sheriff and a few posts from this web site.
My coworkers dont program in C# so they cant help. Im missing something but have no idea what it is.
ViewModel class that inherits from INotifyPropertyChanged:
ParameterSettings _ps;
public ParameterSettings DetailData
{
get { return _ps; }
set
{
_ps = value;
RaisePropertyChanged("DetailData");
}
}
public async Task GetParameters()
{
var pm = new ParameterManager();
DetailData = new ParameterSettings();
await pm.GetLoginCredentials(_ps);
}
this is the code the user control.
ViewModels.ParameterSettingsVm _viewModel;
public ParameterSettingsUc()
{
this.InitializeComponent();
_viewModel = (ParameterSettingsVm)Resources["viewModel"];
var bounds = Window.Current.Bounds;
this.CancelBtn.Width = bounds.Width * .5;
this.SaveBtn.Width = bounds.Width * .5;
}
private async void UserControl_Loaded(object sender, RoutedEventArgs e)
{
await _viewModel.GetParameters();
//UserNameBx.Text = _viewModel.DetailData.UserLogin; //textbox gets filled in.
}
<UserControl
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:SiteManager.Views"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:VM="using:SiteManager.ViewModels"
x:Class="SiteManager.Views.ParameterSettingsUc"
mc:Ignorable="d"
d:DesignHeight="300"
d:DesignWidth="400"
Loaded="UserControl_Loaded">
<UserControl.Resources>
<VM:ParameterSettingsVm x:Key="viewModel"></VM:ParameterSettingsVm>
</UserControl.Resources>
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="50"/>
</Grid.RowDefinitions>
<Grid Grid.Row="0" >
<TextBox Header="Login:" VerticalAlignment="Center" Margin="2,10,0,0" Grid.Row="0" x:Name="UserNameBx" Text="{Binding Path=DetailData.UserLogin, Mode=TwoWay, UpdateSourceTrigger=Default}" >
<TextBox.DataContext>
<VM:ParameterSettingsVm/>
</TextBox.DataContext>
</TextBox>
I would change is it to make the ViewModel a property.
ViewModels.ParameterSettingsVm _viewModel {get;set;}
public ParameterSettingsUc()
{
this.InitializeComponent();
_viewModel = (ParameterSettingsVm)Resources["viewModel"];
var bounds = Window.Current.Bounds;
this.CancelBtn.Width = bounds.Width * .5;
this.SaveBtn.Width = bounds.Width * .5;
}
private async void UserControl_Loaded(object sender, RoutedEventArgs e)
{
await _viewModel.GetParameters();
//UserNameBx.Text = _viewModel.DetailData.UserLogin; //textbox gets filled in.
}
and then I would set the _viewModel as the data context for the textBox. Oh and set the dataContext for the usercontrol to self, like this.
<UserControl
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:SiteManager.Views"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:VM="using:SiteManager.ViewModels"
x:Class="SiteManager.Views.ParameterSettingsUc"
mc:Ignorable="d"
d:DesignHeight="300"
d:DesignWidth="400"
DataContext="{Binding RelativeSource={RelativeSource Self}}"
Loaded="UserControl_Loaded">
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="50"/>
</Grid.RowDefinitions>
<Grid Grid.Row="0" >
<TextBox Header="Login:" VerticalAlignment="Center"
Margin="2,10,0,0" Grid.Row="0" x:Name="UserNameBx"
Text="{Binding Path=DetailData.UserLogin, Mode=TwoWay, UpdateSourceTrigger=Default}"
DataContext={Binding _viewModel}>
</TextBox>
This may not be what you're trying to do though. I just assumed since you're creating _viewModel you would want to use it.
From microsoft virtual academy. Used x:bind its faster and less verbose.
mva.microsoft.com/en-US/training-courses/windows-10-data-binding-14579. Each class property i made into a INotfiyChange property of the vm.
<UserControl
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:SiteManager.Views"
xmlns:VM="using:SiteManager.ViewModels"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:diag="using:System.Diagnostics"
x:Class="SiteManager.Views.ParameterSettingsUc"
mc:Ignorable="d"
d:DesignHeight="300"
d:DesignWidth="400"
Loaded="UserControl_Loaded" >
<UserControl.DataContext>
<VM:ParameterSettingsVm></VM:ParameterSettingsVm>
</UserControl.DataContext>
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="50"/>
</Grid.RowDefinitions>
<Grid Grid.Row="0" >
<Grid.RowDefinitions>
<RowDefinition Height="70"/>
<RowDefinition Height="70"/>
<RowDefinition Height="70"/>
<RowDefinition Height="70"/>
</Grid.RowDefinitions>
<TextBox Header="Login:" VerticalAlignment="Center" Margin="2,10,0,0" Grid.Row="0" x:Name="UserNameBx" Text="{x:Bind Path=_viewModel.UserLogin, Mode=TwoWay }" > </TextBox>
<TextBox Header="Password:" VerticalAlignment="Center" Margin="1" Grid.Row="1" x:Name="PasswordBx" Text="{x:Bind Path=_viewModel.UserPassword, Mode=TwoWay }"> </TextBox>
<TextBox Header="Mature Key:" VerticalAlignment="Center" Margin="1" Grid.Row="2" x:Name="MatureKeyBx" Text="{x:Bind Path=_viewModel.MatureKey, Mode=TwoWay }"> </TextBox>
public sealed partial class ParameterSettingsUc : UserControl
{
ParameterSettingsVm _viewModel { get; set; } = new ParameterSettingsVm();
string _userLogin;
public string UserLogin
{
get { return _userLogin; }
set
{
_userLogin = value;
RaisePropertyChanged("UserLogin");
}
}

One graph take over space when the other is Visibility.Collapsed

This is C# WPF and xaml. I have main window, and I have two graphs that share this window. They are vertically arranged. They both have same width as the main window. However, I want the first graph to fill the entire window (except for some margin on the top of the window) when the second one is collapsed, and I want them to share the height (each with Height = (Height of Window)/2 ) when both are visible.
Here is what I tried in xaml, not successful though:
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<d3:ChartPlotter Grid.Row="0" Name ="timeDomainPlotter" >
</d3:ChartPlotter>
<d3:ChartPlotter Grid.Row="1" Name ="freqDomainPlotter" >
</d3:ChartPlotter>
</Grid>
The first window does not take over the second window's space when the second one is Visibility.Collapsed.
How should I do this? Thanks.
Update:
Converter code in pop up window where there are two graphs:
public class VisibilityToHeightConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
GridLength height = new GridLength();
var visiblity = (Visibility)value;
switch (visiblity)
{
case Visibility.Collapsed:
height = new GridLength(0, GridUnitType.Auto);
break;
case Visibility.Visible:
height = new GridLength(1, GridUnitType.Star);
break;
case Visibility.Hidden:
height = new GridLength(0, GridUnitType.Auto);
break;
}
return height;
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
}
/// <summary>
/// Interaction logic for SignalStatsDisplay.xaml
/// </summary>
public partial class SignalStatsDisplay : Window
{
xaml for pop up window:
<Window x:Class="FileWatcherCaller.View.SignalStatsDisplay"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d3="http://research.microsoft.com/DynamicDataDisplay/1.0"
xmlns:local="clr-namespace:FileWatcherCaller.View"
Title="Real Time Signal Display" Height="409" Width="1200">
<Window.Resources>
<local:VisibilityToHeightConverter x:Key="VisToHeightConv"/>
</Window.Resources>
<Grid>
<StackPanel Orientation="Vertical">
<StackPanel Orientation="Horizontal">
<CheckBox Height="16" HorizontalAlignment="Left" Name="pixVal" VerticalAlignment="Top" Width="120" Checked="checkBox1_Checked">Pixel Value</CheckBox>
<CheckBox Height="16" HorizontalAlignment="Left" Name="roiMean" VerticalAlignment="Top" Width="120">ROI Mean</CheckBox>
<CheckBox Height="16" HorizontalAlignment="Left" Name="roiStd" VerticalAlignment="Top" Width="120" Checked="roiStd_Checked">ROI Std</CheckBox>
</StackPanel>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="{Binding ElementName=timeDomainPlotter, Path=Visibility, Converter={StaticResource VisToHeightConv}}" Name="RowOne" />
<RowDefinition Height="{Binding ElementName=freqDomainPlotter, Path=Visibility, Converter={StaticResource VisToHeightConv}}" Name="RowTwo" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<d3:ChartPlotter Grid.Row="0" Name ="timeDomainPlotter" >
</d3:ChartPlotter>
<d3:ChartPlotter Grid.Row="1" Name ="freqDomainPlotter" >
</d3:ChartPlotter>
</Grid>
</StackPanel>
</Grid>
</Window>
In main window, how the Visibility of two graphs are initialized:
public void StartWatch()
{
if (_fileWatcher != null)
{
_fileWatcher.Dispose();
_fileWatcher = null;
}
if (InitWatcher())
{
this._fileWatcher.Start();
this.ButtonStart.IsEnabled = false;
this.ButtonStop.IsEnabled = true;
}
_signalDisplay = new SignalStatsDisplay();
if (_signalDisplay.Visibility != Visibility.Visible)
{
_signalDisplay.Show();
_signalDisplay.timeDomainPlotter.Visibility = Visibility.Visible;
_signalDisplay.freqDomainPlotter.Visibility = Visibility.Collapsed;
}
}
For Kevin's sulution, I have the xaml for the pop up window:
<Window x:Class="FileWatcherCaller.View.SignalStatsDisplay"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d3="http://research.microsoft.com/DynamicDataDisplay/1.0"
Title="Real Time Signal Display" Height="409" Width="1200">
<Grid>
<StackPanel Orientation="Vertical">
<StackPanel Orientation="Horizontal">
<CheckBox Height="16" HorizontalAlignment="Left" Name="pixVal" VerticalAlignment="Top" Width="120" Checked="checkBox1_Checked">Pixel Value</CheckBox>
<CheckBox Height="16" HorizontalAlignment="Left" Name="roiMean" VerticalAlignment="Top" Width="120">ROI Mean</CheckBox>
<CheckBox Height="16" HorizontalAlignment="Left" Name="roiStd" VerticalAlignment="Top" Width="120" Checked="roiStd_Checked">ROI Std</CheckBox>
</StackPanel>
<UniformGrid Columns="1">
<d3:ChartPlotter Name ="timeDomainPlotter" >
</d3:ChartPlotter>
<d3:ChartPlotter Name ="freqDomainPlotter" >
</d3:ChartPlotter>
</UniformGrid>
</StackPanel>
</Grid>
</Window>
But still, it is not maximize the top D3 graph as expected. It is still takes only half of the window. Anything I should do in the behind code?
<Window x:Class="FileWatcherCaller.View.SignalStatsDisplay"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d3="http://research.microsoft.com/DynamicDataDisplay/1.0"
Title="Real Time Signal Display" Height="409" Width="1200">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition/>
</Grid.RowDefinitions>
<StackPanel Orientation="Horizontal" Grid.Row="0">
<CheckBox Height="16" HorizontalAlignment="Left" Name="pixVal" VerticalAlignment="Top" Width="120" Checked="checkBox1_Checked">Pixel Value</CheckBox>
<CheckBox Height="16" HorizontalAlignment="Left" Name="roiMean" VerticalAlignment="Top" Width="120">ROI Mean</CheckBox>
<CheckBox Height="16" HorizontalAlignment="Left" Name="roiStd" VerticalAlignment="Top" Width="120" Checked="roiStd_Checked">ROI Std</CheckBox>
</StackPanel>
<UniformGrid Columns="1" Grid.Row="1">
<d3:ChartPlotter Name ="timeDomainPlotter" >
</d3:ChartPlotter>
<d3:ChartPlotter Name ="freqDomainPlotter" >
</d3:ChartPlotter>
</UniformGrid>
</Grid>
</Window>
UniformGrid works the way you're looking for (as long as you don't want the user to resize the two sections)
<UniformGrid Columns="1">
<TextBox Visibility="Collapsed">Hello</TextBox>
<TextBox Visibility="Visible">Goodbye</TextBox>
</UniformGrid>
For something more flexible, I think you're going to have to write some code.
Here is an example app that has your desired behavior:
<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">
<Window.Resources>
<local:VisibilityToHeightConverter x:Key="VisToHeightConv"/>
</Window.Resources>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="{Binding ElementName=rctTop, Path=Visibility, Converter={StaticResource VisToHeightConv}}" Name="RowOne" />
<RowDefinition Height="{Binding ElementName=rctBottom, Path=Visibility, Converter={StaticResource VisToHeightConv}}" Name="RowTwo" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Rectangle Fill="Black" Name="rctTop" Grid.Row="0"/>
<Rectangle Fill="Red" Name="rctBottom" Grid.Row="1"/>
</Grid>
</Window>
Code Behind:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
}
public class VisibilityToHeightConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
GridLength height = new GridLength();
var visiblity = (Visibility)value;
switch(visiblity)
{
case Visibility.Collapsed:
height = new GridLength(0, GridUnitType.Auto);
break;
case Visibility.Visible:
height = new GridLength(1, GridUnitType.Star);
break;
case Visibility.Hidden:
height = new GridLength(0, GridUnitType.Auto);
break;
}
return height;
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
}
Let us know if any part of this code is unfamiliar (value converters, binding) and we'll provide an explanation.

Categories