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.
Related
I am trying to insert several StackPanel inside the WrapPanel as you can see the XAML below:
Only the TextBlock inside the StackPanel will be modified so as not to repeat the Title and Text.
<Window x:Class="ambient_test.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:ambient_test"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<Grid>
<ScrollViewer CanContentScroll="True">
<WrapPanel x:Name="wrappanel">
<StackPanel x:Name="panel1" Width="120" Margin="5">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="120"/>
<RowDefinition Height="50"/>
</Grid.RowDefinitions>
<StackPanel Grid.Row="0" Background="#FF38C59F"></StackPanel>
<TextBlock Grid.Row="2" Text="Title 1" Foreground="LightGray" VerticalAlignment="Top" TextAlignment="Center"/>
<TextBlock Grid.Row="1" Text="Text 1" Foreground="#FF747474" TextAlignment="Center" Margin="0 15 0 0"/>
</Grid>
</StackPanel>
<StackPanel x:Name="panel2" Width="120" Margin="5">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="120"/>
<RowDefinition Height="50"/>
</Grid.RowDefinitions>
<StackPanel Grid.Row="0" Background="#FF38C59F"></StackPanel>
<TextBlock Grid.Row="2" Text="Title 2" Foreground="LightGray" VerticalAlignment="Top" TextAlignment="Center"/>
<TextBlock Grid.Row="1" Text="Text 2" Foreground="#FF747474" TextAlignment="Center" Margin="0 15 0 0"/>
</Grid>
</StackPanel>
</WrapPanel>
</ScrollViewer>
</Grid>
</Window>
I created a class called Info to change the Title and the Text. In the method constructor I have a for loop that will add 10 contents to the List.
public MainWindow()
{
InitializeComponent();
List<Info> infos = new List<Info>();
for (int i = 0; i < 10; i++)
{
infos.Add(new Info()
{
Title = $"title {i}",
Text = $"text {i}"
});
}
}
public class Info
{
public string Title { get; set; }
public string Text { get; set; }
}
For example, my List has 100 records, so I need to add 100 StackPanel inside my WrapPanel following the same Title and Text as the List.
Is there any way to do this? For example using Binding?
You would use an ItemsControl that uses a WrapPanel as its ItemsPanel, and defines the layout of the items by an appropriate DataTemplate:
<ItemsControl x:Name="itemsControl">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="120"/>
<RowDefinition Height="50"/>
</Grid.RowDefinitions>
<TextBlock Grid.Row="0" Text="{Binding Title}" .../>
<TextBlock Grid.Row="1" Text="{Binding Text}" .../>
</Grid>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
and assign the List<Info> to its ItemsSource property:
itemsControl.ItemsSource = infos;
Im currently trying to use the AdaptiveGrid in the toolkit to display Some images that then can be clicked on to lead to pages, I think this ItemTemplate is what provides the images in the example. Is this the same as any DataTemplate? I cant find any information online about these or docs.
<Controls:AdaptiveGridView Name="AdaptiveGridViewControl"
OneRowModeEnabled="False"
ItemHeight="200"
DesiredWidth="300"
SelectionMode="Single"
IsItemClickEnabled="True"
ItemTemplate="{StaticResource PhotosTemplate}"/>
I've tried the following, I get no errors but nothing shows up when I run it locally except the command bar:
Mainpage.xaml:
<Page
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:MobileAppProject"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:viewModels="using:ViewModels"
xmlns:Controls="using:Microsoft.Toolkit.Uwp.UI.Controls"
x:Class="MobileAppProject.MainPage"
mc:Ignorable="d">
<Page.Resources>
<DataTemplate x:Key="AdaptTemplate">
<Grid
Background="White"
BorderBrush="Black"
BorderThickness="1">
<Image
Source="{Binding Image}"
Stretch="UniformToFill"
HorizontalAlignment="Center"
VerticalAlignment="Center"/>
</Grid>
</DataTemplate>
</Page.Resources>
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<ScrollViewer VerticalScrollMode="Auto" VerticalScrollBarVisibility="Auto">
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}" Margin="12,10,12,12">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<StackPanel Margin="0,0,0,10"/>
<Grid Grid.Row="1">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition/>
</Grid.RowDefinitions>
<Controls:AdaptiveGridView Name="AdaptiveGridViewControl"
OneRowModeEnabled="False"
ItemHeight="200"
DesiredWidth="300"
SelectionMode="Single"
IsItemClickEnabled="True"
ItemTemplate="{StaticResource AdaptTemplate}"/>
<StackPanel VerticalAlignment="Bottom" Margin="0,24,0,0" Grid.Row="1" Background="{ThemeResource SystemControlBackgroundAccentBrush}">
<CommandBar x:Name="cmdbar"
IsOpen="{Binding IsChecked, ElementName=isopentoggle, Mode=TwoWay}"
IsSticky="{Binding IsChecked, ElementName=isstickytoggle, Mode=TwoWay}"
ClosedDisplayMode="{Binding SelectedItem.Content, ElementName=combobox}">
<CommandBar.SecondaryCommands>
<AppBarButton Label="Menu Item 1"/>
<AppBarButton Label="Menu Item 2"/>
<AppBarButton Label="Menu Item 3"/>
<AppBarButton Label="Menu Item 4"/>
</CommandBar.SecondaryCommands>
<AppBarButton Icon="Accept" Label="Accept"/>
<AppBarToggleButton Icon="Contact" Label="Contact"/>
</CommandBar>
<Image HorizontalAlignment="Left" Source="Assets/storeLogo-sdk.png" Stretch="None"/>
</StackPanel>
</Grid>
<!-- Status Block for providing messages to the user. Use the
NotifyUser() method to populate the message -->
<TextBlock x:Name="StatusBlock" Grid.Row="2" Margin="12, 10, 12, 10" Visibility="Collapsed"/>
</Grid>
</ScrollViewer>
</Grid>
</Page>
Mainpage.xaml.cs:
private ObservableCollection<AdaptItem> picItems_;
private ObservableCollection<AdaptItem> PicItems
{
get
{
return picItems_;
}
set
{
picItems_ = value;
}
}
public MainPage()
{
this.InitializeComponent();
picItems_ = AdaptItem.AdaptList();
this.DataContext = PicItems;
}
AdaptTemplate.cs for filling the AdaptGrid:
public class AdaptItem
{
public String Image
{
get;
set;
}
}
public static ObservableCollection<AdaptItem> AdaptList()
{
ObservableCollection<AdaptItem> pics = new ObservableCollection<AdaptItem>()
{
new AdaptItem
{
Image = "Assets/01.jpg"
},
new AdaptItem
{
Image = "Assets/02.jpg"
},
new AdaptItem
{
Image = "Assets/03.jpg"
},
new AdaptItem
{
Image = "Assets/04.jpg"
},
new AdaptItem
{
Image = "Assets/05.jpg"
}
};
return pics;
}
You can check how the template is used in the UWP Toolkit sample app or in the sample code on GitHub:
<Controls:AdaptiveGridView Name="AdaptiveGridViewControl"
OneRowModeEnabled="False"
ItemHeight="200"
DesiredWidth="300"
SelectionMode="Single"
IsItemClickEnabled="True"
ItemTemplate="{StaticResource PhotosTemplate}">
<Controls:AdaptiveGridView.Resources>
<DataTemplate x:Key="PhotosTemplate">
<Grid
Background="White"
BorderBrush="Black"
BorderThickness="1">
<Image
Source="{Binding Thumbnail}"
Stretch="UniformToFill"
HorizontalAlignment="Center"
VerticalAlignment="Center"/>
</Grid>
</DataTemplate>
</Controls:AdaptiveGridView.Resources>
</Controls:AdaptiveGridView>
In short - yes, the ItemTemplate works the same way as any other DataTemplate.
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();
}
}
I implemented the solution for this question, in a window with XAML given below. I am trying to make a scrolling marquee text effect for a label:
<Window x:Class="WpfMarqueeText.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:wpfMarqueeText="clr-namespace:WpfMarqueeText"
Title="MainWindow" Height="350" Width="525">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="200"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="500"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Grid Grid.Row="0" Grid.Column="0" Background="Aqua">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="200"/>
<ColumnDefinition Width="300"/>
</Grid.ColumnDefinitions>
<Ellipse Grid.Column="0" Margin="5,3,5,3" Fill="#b933ad"/>
<Label Grid.Column="0" Content="Z" Foreground="White" FontFamily="HelveticaBold" FontSize="150" FontWeight="Bold" VerticalAlignment="Center" HorizontalAlignment="Center" Margin="5,3,5,3"/>
<Grid Grid.Column="1">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Grid Grid.Row="0">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Label Grid.Row="0" Grid.Column="0" Content="Some Info:" FontFamily="HelveticaBold" FontSize="18" FontWeight="Bold" Margin="5,3,5,3"/>
<StackPanel Grid.Row="0" Grid.Column="1" Orientation="Horizontal" x:Name="stack">
<StackPanel.Resources>
<wpfMarqueeText:NegatingConverter x:Key="NegatingConverter" />
<Storyboard x:Key="slide">
<DoubleAnimation From="0" To="{Binding Width, ElementName=canvas, Converter={StaticResource NegatingConverter}}" Duration="00:00:10"
Storyboard.TargetProperty="X"
Storyboard.TargetName="transferCurreny"
RepeatBehavior="Forever"/>
</Storyboard>
</StackPanel.Resources>
<StackPanel.RenderTransform>
<TranslateTransform x:Name="transferCurreny" X="0"/>
</StackPanel.RenderTransform>
<StackPanel.Triggers>
<EventTrigger RoutedEvent="StackPanel.Loaded">
<BeginStoryboard Storyboard="{StaticResource slide}" />
</EventTrigger>
<EventTrigger RoutedEvent="StackPanel.SizeChanged">
<BeginStoryboard Storyboard="{StaticResource slide}" />
</EventTrigger>
</StackPanel.Triggers>
<Canvas x:Name="canvas" Width="{Binding ActualWidth, ElementName=stack}">
<Label FontFamily="HelveticaBold" FontSize="18" Margin="5,3,5,3" x:Name="Label1" Content="Blah blah blah" Canvas.Left="0"/>
<Label Name="Label2" Content="{Binding Content, ElementName=Label1}" FontFamily="HelveticaBold" FontSize="18" Margin="5,3,5,3" Canvas.Left="{Binding ActualWidth, ElementName=stack}"/>
</Canvas>
</StackPanel>
</Grid>
</Grid>
</Grid>
</Grid>
</Window>
You must also define the NegatingConverter class in the code-behind:
public class NegatingConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value is double)
{
return -((double)value);
}
return value;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value is double)
{
return +(double)value;
}
return value;
}
}
This produces the desired efect, however the text animation scrolls over the other UI elements as seen in the images below (sorry, I don't have enough rep to post images):
http://tinypic.com/r/df8zeu/9
http://tinypic.com/r/2inc3r/9
So, is there any way to fix the animation so that the text only scrolls within the boundary of the grid column it is contained within, or within the boundaries of the label itself? Thanks for any help!
This is a quick and dirty-solution:
Change your Label to
<Label Grid.Row="0" Grid.Column="0" Content="Some Info:" FontFamily="HelveticaBold" FontSize="18" FontWeight="Bold" Margin="5,3,5,3" Panel.ZIndex="99" Background="Aqua"/>
The Panel.ZIndex brings the Label to front. And making the Background not Transparent gives the desired look. The boundries are still not perfect, but this should give you a clue on how to handle Layers
The article linked by Akanksha in response to my OP shows how to create a user control that produces a clean, scrolling text effect. You can also specify 4 different scroll directions, left <-> right and up <-> down. I'll give my implementation here for others:
XAML for MarqueeTextUserControl:
<UserControl x:Class="AaronLuna.Common.UI.UserControls.MarqueeTextUserControl"
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" Loaded="UserControl_Loaded">
<Canvas ClipToBounds="True" Name="CanvasMain">
<TextBlock Name="TextBlockMain"/>
</Canvas>
</UserControl>
Code for MarqueeTextUserControl:
namespace AaronLuna.Common.UI.UserControls
{
public partial class MarqueeTextUserControl
{
public MarqueeTextUserControl()
{
InitializeComponent();
CanvasMain.Height = Height;
CanvasMain.Width = Width;
}
public ScrollDirection ScrollDirection { get; set; }
public double ScrollDurationInSeconds { get; set; }
public String Text { set { TextBlockMain.Text = value; }}
private void UserControl_Loaded(object sender, RoutedEventArgs e)
{
ScrollText(ScrollDirection);
}
public void ScrollText(ScrollDirection scrollDirection)
{
switch (scrollDirection)
{
case ScrollDirection.LeftToRight:
LeftToRightMarquee();
break;
case ScrollDirection.RightToLeft:
RightToLeftMarquee();
break;
case ScrollDirection.TopToBottom:
TopToBottomMarquee();
break;
case ScrollDirection.BottomToTop:
BottomToTopMarquee();
break;
}
}
private void LeftToRightMarquee()
{
double height = CanvasMain.ActualHeight - TextBlockMain.ActualHeight;
TextBlockMain.Margin = new Thickness(0, height/2, 0, 0);
var doubleAnimation = new DoubleAnimation
{
From = -TextBlockMain.ActualWidth,
To = CanvasMain.ActualWidth,
RepeatBehavior = RepeatBehavior.Forever,
Duration = new Duration(TimeSpan.FromSeconds(ScrollDurationInSeconds))
};
TextBlockMain.BeginAnimation(Canvas.LeftProperty, doubleAnimation);
}
private void RightToLeftMarquee()
{
double height = CanvasMain.ActualHeight - TextBlockMain.ActualHeight;
TextBlockMain.Margin = new Thickness(0, height/2, 0, 0);
var doubleAnimation = new DoubleAnimation
{
From = -TextBlockMain.ActualWidth,
To = CanvasMain.ActualWidth,
RepeatBehavior = RepeatBehavior.Forever,
Duration = new Duration(TimeSpan.FromSeconds(ScrollDurationInSeconds))
};
TextBlockMain.BeginAnimation(Canvas.RightProperty, doubleAnimation);
}
private void TopToBottomMarquee()
{
double width = CanvasMain.ActualWidth - TextBlockMain.ActualWidth;
TextBlockMain.Margin = new Thickness(width/2, 0, 0, 0);
var doubleAnimation = new DoubleAnimation
{
From = -TextBlockMain.ActualHeight,
To = CanvasMain.ActualHeight,
RepeatBehavior = RepeatBehavior.Forever,
Duration = new Duration(TimeSpan.FromSeconds(ScrollDurationInSeconds))
};
TextBlockMain.BeginAnimation(Canvas.TopProperty, doubleAnimation);
}
private void BottomToTopMarquee()
{
double width = CanvasMain.ActualWidth - TextBlockMain.ActualWidth;
TextBlockMain.Margin = new Thickness(width/2, 0, 0, 0);
var doubleAnimation = new DoubleAnimation
{
From = -TextBlockMain.ActualHeight,
To = CanvasMain.ActualHeight,
RepeatBehavior = RepeatBehavior.Forever,
Duration = new Duration(TimeSpan.FromSeconds(ScrollDurationInSeconds))
};
TextBlockMain.BeginAnimation(Canvas.BottomProperty, doubleAnimation);
}
}
public enum ScrollDirection
{
LeftToRight,
RightToLeft,
TopToBottom,
BottomToTop
}
}
Client XAML:
<UserControl x:Class="MarqueeTextExampleUserControl"
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:userControls="clr-namespace:AaronLuna.Common.UI.UserControls;assembly=AaronLuna.Common"
mc:Ignorable="d">
<DockPanel>
<Label Content="Some Info:"/>
<userControls:MarqueeTextUserControl x:Name="MarqueeTextBlock"/>
</DockPanel>
</UserControl>
Client Code:
MarqueeTextBlock.Text = "Blah blah blah";
MarqueeTextBlock.ScrollDirection = ScrollDirection.RightToLeft;
MarqueeTextBlock.ScrollDurationInSeconds = 10;
I'm creating a win8 app and I need to change the layout of my grid so everything fits on screen when the user flips between orientations. I understand I need to use VisualStateManager but I can't understand any tutorials. If I have this code:
<Grid>
<Button x:Name="button1" Margin="188,73,286,0" VerticalAlignment="Top"/>
<Button x:Name="button2" Margin="236,73,238,0" VerticalAlignment="Top"/>
<Button x:Name="button3" Margin="284,73,190,0" VerticalAlignment="Top"/>
</Grid>
How would I use visual state manager to change the buttons so they are now orientated in a column (one above the other) instead of in a row when the orientation is changed to portrait from landscape?
Thanks
xaml code
<Page
x:Class="App1.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:App1"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d" SizeChanged="Page_SizeChanged_1">
<Grid Background="{StaticResource ApplicationPageBackgroundThemeBrush}">
<Grid x:Name="FullScreenLandscape">
<Grid.ColumnDefinitions>
<ColumnDefinition></ColumnDefinition>
<ColumnDefinition></ColumnDefinition>
<ColumnDefinition></ColumnDefinition>
</Grid.ColumnDefinitions>
<Button Content="Button1" HorizontalAlignment="Center" VerticalAlignment="Center"></Button>
<Button Content="Button2" Grid.Column="1" HorizontalAlignment="Center" VerticalAlignment="Center"></Button>
<Button Content="Button3" Grid.Column="2" HorizontalAlignment="Center" VerticalAlignment="Center"></Button>
</Grid>
<Grid x:Name="Snapped">
<Grid.RowDefinitions>
<RowDefinition></RowDefinition>
<RowDefinition></RowDefinition>
<RowDefinition></RowDefinition>
</Grid.RowDefinitions>
<Button Content="Button1" HorizontalAlignment="Center" VerticalAlignment="Center"></Button>
<Button Content="Button2" Grid.Row="1" HorizontalAlignment="Center" VerticalAlignment="Center"></Button>
<Button Content="Button3" Grid.Row="2" HorizontalAlignment="Center" VerticalAlignment="Center"></Button>
</Grid>
</Grid>
C# code
private void Page_SizeChanged_1(object sender, SizeChangedEventArgs e)
{
switch (ApplicationView.Value)
{
case ApplicationViewState.FullScreenLandscape:
VisualStateManager.GoToState(this, "FullScreenLandscape", false);
Snapped.Visibility = Visibility.Collapsed;
break;
case ApplicationViewState.Snapped:
VisualStateManager.GoToState(this, "Snapped", false);
FullScreenLandscape.Visibility = Visibility.Collapsed;
Snapped.Visibility = Visibility.Visible;
break;
default:
return;
}
}
Another Method
xaml code
<Grid Background="{StaticResource ApplicationPageBackgroundThemeBrush}">
<Grid x:Name="landscape">
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<TextBox x:Name="t1" Grid.Column="0" FontSize="24" Height="100"/>
<TextBox x:Name="t2" Grid.Column="1" FontSize="24" Height="100"/>
<TextBox x:Name="t3" Grid.Column="2" FontSize="24" Height="100"/>
</Grid>
<Grid x:Name="snap" Visibility="Collapsed">
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition/>
</Grid.RowDefinitions>
</Grid>
</Grid>
C# code
private void Page_SizeChanged_1(object sender, SizeChangedEventArgs e)
{
if (Windows.UI.ViewManagement.ApplicationView.Value == Windows.UI.ViewManagement.ApplicationViewState.FullScreenLandscape)
{
landscape.Visibility = Windows.UI.Xaml.Visibility.Visible;
snap.Visibility = Windows.UI.Xaml.Visibility.Collapsed;
t1.SetValue(Grid.ColumnProperty, 0);
t2.SetValue(Grid.ColumnProperty, 1);
if (t1.Parent != landscape)
{
snap.Children.Remove(t1);
snap.Children.Remove(t2);
landscape.Children.Add(t1);
landscape.Children.Add(t2);
}
}
else if(Windows.UI.ViewManagement.ApplicationView.Value == Windows.UI.ViewManagement.ApplicationViewState.Snapped)
{
landscape.Children.Remove(t1);
landscape.Children.Remove(t2);
landscape.Visibility = Windows.UI.Xaml.Visibility.Collapsed;
snap.Visibility = Windows.UI.Xaml.Visibility.Visible;
t1.SetValue(Grid.RowProperty, 0);
t2.SetValue(Grid.RowProperty, 1);
if (t1.Parent != snap)
{
landscape.Children.Remove(t1);
landscape.Children.Remove(t2);
snap.Children.Add(t1);
snap.Children.Add(t2);
}
}
}