How to find UserControl width in WPF? - c#

<UserControl x:Class="JIMS.View.Settings.Settings"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Name="SettingsWindow">
<Border Style="{StaticResource WindowBorderStyle}" Height="100">
<StackPanel Orientation="Vertical">
<my:TitleBar Title="settings"/>
<StackPanel Orientation="Horizontal">
<StackPanel Orientation="Horizontal">
<StackPanel Orientation="Vertical" Margin="10,0,0,0">
<Label>Theme :</Label>
</StackPanel>
<StackPanel Orientation="Vertical" Width="200" Margin="0,0,10,0">
<ComboBox Name="cmbTheme" ItemsSource="{Binding Path=Themes}" ></ComboBox>
</StackPanel>
</StackPanel>
</StackPanel>
</StackPanel>
</Border>
</UserControl>
This is my UserControl and i have not set its Width and Height properties.
But in some codebehind i want to get the Height and width of this UserControl and i am not able to get them.
double width=uctrl.Width;
It gives me NaN while
double width=ctrl.ActualWidth;
giving me 0
The code where i need width and height
private void OpenChild(UserControl ctrl)
{
bool alreadyExist = false;
ctrl.Uid = ctrl.Name;
foreach (UIElement child in JIMSCanvas.Children)
{
if (child.Uid == ctrl.Uid)
{
alreadyExist = true;
Canvas.SetZIndex(child, GetMaxZIndex);
}
}
if (!alreadyExist)
{
JIMSCanvas.Children.Add(ctrl);
JIMSCanvas.InvalidateMeasure();
double top = (JIMSCanvas.ActualHeight - ctrl.Height) / 2;
double left = (JIMSCanvas.ActualWidth - ctrl.Width) / 2;
Canvas.SetLeft(ctrl, left);
Canvas.SetTop(ctrl, top);
}
}

In a brand new project I wrote this (and it gave me updated width when changing the Window):
<Window xmlns:my="clr-namespace:WpfApplication2"
x:Class="WpfApplication2.Window32"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Window32"
Height="300"
Width="300">
<Grid>
<my:UserControl3 x:Name="uc3" />
<TextBlock Height="23"
HorizontalAlignment="Left"
Margin="126,121,0,0"
Name="textBlock1"
Text="{Binding ElementName=uc3, Path=ActualWidth}"
VerticalAlignment="Top" />
</Grid>
</Window>

I solved the Issue with this code...
private void OpenChild(UserControl ctrl)
{
bool alreadyExist = false;
ctrl.Uid = ctrl.Name;
foreach (UIElement child in JIMSCanvas.Children)
{
if (child.Uid == ctrl.Uid)
{
alreadyExist = true;
Canvas.SetZIndex(child, GetMaxZIndex);
}
}
if (!alreadyExist)
{
JIMSCanvas.Children.Add(ctrl);
JIMSCanvas.UpdateLayout();
double top = (JIMSCanvas.ActualHeight - ctrl.ActualHeight) / 2;
double left = (JIMSCanvas.ActualWidth - ctrl.ActualWidth) / 2;
Canvas.SetLeft(ctrl, left);
Canvas.SetTop(ctrl, top);
}
}
I have used JIMSCanvas.UpdateLayout();
Thank all for helping and especially #NestorArturo

Related

How to make a Popup appear in a specific corner

I want my Popup to always appear in a specific corner (lower right corner e.g.) no matter the size and the resolution of my View.
I tried using HorizontalAlignment and VerticalAlignment but it's not really working.
Here's my code :
<Grid x:Name="Output" HorizontalAlignment="Right" VerticalAlignment="Bottom" Grid.Row="1">
<Popup x:Name="StandardPopup">
<Border BorderBrush="{StaticResource ApplicationForegroundThemeBrush}"
Background="{StaticResource ApplicationPageBackgroundThemeBrush}"
BorderThickness="2" Width="500" Height="500">
<StackPanel HorizontalAlignment="Center" VerticalAlignment="Center">
<WebView x:Name="WebView1" Source="https://www.bing.com/" Width="490" Height="490" HorizontalAlignment="Center"/>
</StackPanel>
</Border>
</Popup>
</Grid>
You can create a UserControl to help you achieve this.
Create a new UserControl called TestPopup
<UserControl
...
>
<Grid>
<Grid HorizontalAlignment="Right" VerticalAlignment="Bottom">
<Border BorderBrush="{StaticResource ApplicationForegroundThemeBrush}"
Background="{StaticResource ApplicationPageBackgroundThemeBrush}"
BorderThickness="2" Width="500" Height="500">
<StackPanel HorizontalAlignment="Center" VerticalAlignment="Center">
<WebView x:Name="WebView1" Source="https://www.bing.com/" Width="490" Height="490" HorizontalAlignment="Center"/>
</StackPanel>
</Border>
</Grid>
</Grid>
</UserControl>
The code-behind:
public sealed partial class TestPopup : UserControl
{
private Popup _popup = null;
public TestPopup()
{
this.InitializeComponent();
// If you need to top placement, please comment out the Width/Height statement below
this.Width = Window.Current.Bounds.Width;
this.Height = Window.Current.Bounds.Height;
//Assign the current control to the Child property of the popup. The Child property is what the popup needs to display.
_popup = new Popup();
_popup.Child = this;
}
public void ShowPopup()
{
_popup.IsOpen = true;
}
public void HidePopup()
{
_popup.IsOpen = false;
}
}
Use C# code references where needed
public sealed partial class MainPage : Page
{
private TestPopup _popup = new TestPopup();
public MainPage()
{
this.InitializeComponent();
_popup.ShowPopup();
}
}
To ensure that it is in the lower right corner at any width, you can listen to the page's SizeChanged event.
private void Page_SizeChanged(object sender, SizeChangedEventArgs e)
{
_popup.Width = Window.Current.Bounds.Width;
_popup.Height = Window.Current.Bounds.Height;
// If you need to use the Width/Height of the page
// _popup.Width = e.NewSize.Width;
// _popup.Height = e.NewSize.Height;
}
Best regards.
Sadly Popup does not support Alignments you have to use the Offset Values
NOTE: Don't forget that you can use Bindings for Offsets and so get your wanted behavior with some Converter magic.
<Popup HorizontalOffset="20"
VerticalOffset="10">
<!--Content-->
</Popup>

Bind to code behind

I'm reworking a very old control to a MVVM-kind of control. I have a list of alarms. When the user presses the button in the column header, I have to clear the list of visible alarms and scroll to the next alarm (so the first one which was not visible).
I created the button in the control template of the column header. The command property works but it return a NaN, so I expect that the binding of the command parameter to the Height of the visible part of the window is incorrect. When I debug the code behind, the property "Height" does hold a number.
The XAML:
<DataGrid x:Class="Kwa.Presentation.Views.AlarmList.AlarmList"
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:Kwa.Presentation.Views.AlarmList"
xmlns:components="clr-namespace:Kwa.Presentation.Components"
xmlns:converters="clr-namespace:Kwa.Presentation.Converters"
xmlns:Trans="clr-namespace:Kwa.Presentation.Resources"
mc:Ignorable="d"
d:DesignHeight="500" d:DesignWidth="750"
ItemsSource="{Binding Alarms}"
SelectedItem="{Binding SelectedAlarm}"
IsSynchronizedWithCurrentItem="True"
CanUserResizeColumns="True" IsReadOnly="True" CanUserReorderColumns="False" CanUserSortColumns="False" SelectionMode="Single" CanUserAddRows="False"
Background="White" RowHeaderWidth="0" AutoGenerateColumns="False" GridLinesVisibility="None" RowHeight="{Binding Rowheight}" FrozenColumnCount = "1"
ScrollViewer.CanContentScroll="True" ScrollViewer.VerticalScrollBarVisibility="Auto" ScrollViewer.HorizontalScrollBarVisibility="Auto"
x:Name="AlarmFramework"
SizeChanged="AlarmFramework_SizeChanged"
>
<Style TargetType="DataGridColumnHeader" x:Key="WithButt">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="DataGridColumnHeader">
<Border BorderBrush="Silver" BorderThickness="0 0 0 1"
Padding="5 0 0 0" Background="White">
<StackPanel Orientation="Horizontal" Margin="0">
<TextBlock Text="{Binding}"
VerticalAlignment="Center" FontWeight="Bold"/>
<Button Content="{x:Static Trans:TranslatedResources.AlarmAcceptContent}" Margin="60 3 10 3 " VerticalAlignment="Center" VerticalContentAlignment="Center" Padding="2"
Command="{Binding DataContext.AcknowledgeCommand, RelativeSource={RelativeSource AncestorType={x:Type local:AlarmList}}}" CommandParameter="{Binding Height, RelativeSource={RelativeSource AncestorType={x:Type local:AlarmList}}}" ToolTip="{x:Static Trans:TranslatedResources.AlarmAcceptTooltip}" Style="{StaticResource Butt}"/>
</StackPanel>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</DataGrid>
The Code behind:
public partial class AlarmList : DataGrid
{
private double Height = 0;
public AlarmList()
{
InitializeComponent();
}
private void AlarmFramework_SizeChanged(object sender, SizeChangedEventArgs e)
{
Height = e.NewSize.Height;
}
}
The ViewModel:
public class AlarmListViewModel : MainViewModelBase
{
private readonly IActionCommand _acknowledgeCommand;
public IActionCommand AcknowledgeCommand
{
get { return _acknowledgeCommand; }
}
public AlarmListViewModel()
{
//Add command
_acknowledgeCommand = new ActionCommand<double>(p => Acknowledge(p));
}
private void Acknowledge(double parameter)
{
try
{
double DatagridWidth = (double)parameter;
int AmountAcknowledged = (int)Math.Floor(DatagridWidth / RowHeight);
int LastAlarmSent = Alarms[0].AlarmNumber + AmountAcknowledged;
_proxy.Send(LastAlarmSent);
SelectedAlarm = Alarms[LastAlarmSent + 1];
}
catch (Exception ex)
{
_viewManager.ShowDialog(new MessageDialogViewModel()
{
AskAnswer = false,
Text = ex.Message,
Title = TranslatedResources.AlarmAckSendErrorTitle,
});
}
}
}
I think if you initialize your property with usercontrol it will works
public AlarmList()
{
InitializeComponent();
Height = this.ActualHeight;
}
Or change your CommandParameter like this:
CommandParameter="{Binding ActualHeight .....

Get control inside DataTemplate

So I have a FlipView defined in xaml by the following code:
<FlipView x:Name="carrousel" Height="175" Background="Transparent" Margin="0,20,0,0">
<FlipView.ItemTemplate>
<DataTemplate>
<Grid>
<Rectangle x:Name="profile" Stroke="White" StrokeThickness="0" HorizontalAlignment="Center" VerticalAlignment="Center" Width="175" Height="175" Canvas.ZIndex="1" RadiusX="88" RadiusY="88" Tapped="profile_Tapped"/>
</Grid>
</DataTemplate>
</FlipView.ItemTemplate>
</FlipView>
When a user clicks on the rectangle, it's animated to become bigger, but I also want all the other rectangles of every other FlipViewItem to change size too. How can I achieve this? I tried:
foreach(FlipViewItem fvi in carrousel.Items)
{
Rectangle g = (fvi.Content as Grid).FindName("profile") as Rectangle;
g.Width = double;
g.Height = double;
}
But seeing as my flipview doesn't contain FlipViewItems but custom classes I've binded to it (which obviously have no .Content), it doesn't work. How can I get this to work?
foreach(var fvi in carrousel.Items)
{
FlipViewItem item=carrousel.ContainerFromItem(fvi);
var rectangle =FindElementInVisualTree<Rectangle>(item);
//Or without VisualTreeHelper you can do like what were you trying before
Rectangle g = (item.Content as Grid).FindName("profile") as Rectangle;
g.Width = double;
g.Height = double;
}
private T FindElementInVisualTree<T>(DependencyObject parentElement) where T : DependencyObject
{
var count = VisualTreeHelper.GetChildrenCount(parentElement);
if (count == 0) return null;
for (int i = 0; i < count; i++)
{
var child = VisualTreeHelper.GetChild(parentElement, i);
if (child != null && child is T)
return (T)child;
else
{
var result = FindElementInVisualTree<T>(child);
if (result != null)
return result;
}
}
return null;
}
This solution is a turn around solution (because I could not manage the relative binding).
<Grid Background="{ThemeResource SystemControlBackgroundListMediumBrush}">
<StackPanel Margin="100,10,10,10">
<FlipView x:Name="carrousel" Height="350" Width="350" Background="Red" Margin="0,20,0,0">
<FlipView.ItemTemplate>
<DataTemplate>
<Grid>
<Rectangle x:Name="profile" Stroke="White" StrokeThickness="1" Fill="Aqua"
HorizontalAlignment="Center" VerticalAlignment="Center"
Width="{Binding ElementName=RefValueRect, Path=Width, Mode=OneWay}"
Height="{Binding ElementName=RefValueRect, Path=Height, Mode=OneWay}" Canvas.ZIndex="1" RadiusX="88"
RadiusY="88" Tapped="profile_Tapped"/>
</Grid>
</DataTemplate>
</FlipView.ItemTemplate>
</FlipView>
</StackPanel>
<Rectangle x:Name="RefValueRect" Width="175" Height="175" Visibility="Collapsed" />
</Grid>
code behind
private void profile_Tapped(object sender, TappedRoutedEventArgs e)
{
RefValueRect.Width *= 2;
RefValueRect.Height *= 2;
}

C# WPF vertical listBox not showing vertical scrollbar

I have a listBox with some elements in it.
<StackPanel Orientation="Vertical" Grid.Row="0" >
<StackPanel Orientation="Horizontal" HorizontalAlignment="Left">
<TextBlock x:Name="lbGroups" Text="PartPrograms Groups" FontSize="{StaticResource TEXTBOX_TITLE_FONTSIZE}" FontWeight="Bold" Margin="20" HorizontalAlignment="Left" VerticalAlignment="Center" TextAlignment="Center" Grid.Row="1"/>
<Button x:Name="btAddGroup" Content="" FontSize="{StaticResource TEXTBOX_BIGBUTTON_FONTSIZE}" Background="{x:Null}" BorderBrush="{x:Null}" Click="Button_Click"/>
<Button Name="btDeleteGroup" Content="" FontSize="{StaticResource TEXTBOX_BIGBUTTON_FONTSIZE}" Background="{x:Null}" BorderBrush="{x:Null}" Click="Button_Click"/>
<Button x:Name="btGroupDown" Content="" FontSize="{StaticResource TEXTBOX_BIGBUTTON_FONTSIZE}" Background="{x:Null}" BorderBrush="{x:Null}" Click="Button_Click"/>
<Button Name="btGroupUp" Content="" FontSize="{StaticResource TEXTBOX_BIGBUTTON_FONTSIZE}" Background="{x:Null}" BorderBrush="{x:Null}" Click="Button_Click"/>
</StackPanel>
<ListBox Name="lbPPgroups" Background="{x:Null}" Margin="0" ScrollViewer.VerticalScrollBarVisibility="Visible">
</ListBox> <------- this is the listbox
</StackPanel>
The elements are programmatically added to the listBox with this:
void AddNewPartProgramGroup(String strContent, String strNotes, String strPathImage, bool IsChecked=false)
{
StackPanel sp = new StackPanel();
string currentDir = AppDomain.CurrentDomain.BaseDirectory.ToString();
ToggleButton toggleButton = new ToggleButton()
{
Content = strContent,
Height = IMAGES_ROW_HEIGHT / GOLDEN_RATIO,
Width = IMAGES_ROW_HEIGHT,
FontSize = 10,
Background = null,
Tag = "bt" + strContent,
ToolTip = strNotes,
Margin = new Thickness(BUTTON_MARGIN),
IsChecked = IsChecked
};
toggleButton.Click += new RoutedEventHandler(ToggleButton_Click);
sp.Children.Add(toggleButton);
Image newResizedImage = ImageUtilities.StrPath2ResizedImageSizeHeight(strPathImage, IMAGES_ROW_HEIGHT);
sp.Children.Add(newResizedImage);
sp.Orientation = Orientation.Horizontal;
sp.HorizontalAlignment = HorizontalAlignment.Left;
this.lbPPgroups.Items.Add(sp);<------ here I add elements
var newGroup = new PcDmisData.Group();
newGroup.Description = strContent;
var newImage = new PcDmisData.MyImage();
newImage.Image = newResizedImage;
newImage.IsImageEmbedded = false;
newGroup.myImage = newImage;
newGroup.Notes = strNotes;
EasyRunData.lstPPgroups.Add(newGroup);
}
the problem is after adding some elements I can't see the vertical scrollbar on the listbox:
I also tried to add a vertical scroll viewer but that didn't work.
Thanx for any help
PAtrick
So the problem is that the outer StackPanel has no real MaxHeight and the Height updates automatically. The ScrollBar only appears if this Panel reaches a certain limit in its heigth. To solve this you could play around with MaxHeight...
I would recommend to use a DockPanel.
<Grid>
<DockPanel Grid.Row="0" >
<StackPanel DockPanel.Dock="Top" Orientation="Horizontal" HorizontalAlignment="Left">
<TextBlock x:Name="lbGroups" Text="PartPrograms Groups" FontWeight="Bold" Margin="20" HorizontalAlignment="Left" VerticalAlignment="Center" TextAlignment="Center"/>
<Button Name="btGroupUp" Click="btGroupUp_Click" Margin="2,2,2,2" Width="30"/>
</StackPanel>
<ListBox Name="lbPPgroups" Margin="0" ScrollViewer.VerticalScrollBarVisibility="Auto"/>
</DockPanel>
</Grid>
Just for the example in code behind:
private void btGroupUp_Click(object sender, RoutedEventArgs e)
{
for (var i=1;i<50;i++)
{
TextBox box = new TextBox();
box.Text = "Hello World " + i ;
lbPPgroups.Items.Add(box);
}
}
In this example i set ScrollViewer.VerticalScrollBarVisibility="Auto" so the ScrollBar only appears when it is needed. But you can also set it to "Visible".

Find the Fill value of a rectangle inside a comboboxItem

I have following xaml code:
<Window x:Class="WPF_les_3.Oefening_4"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Oefening_4" Height="300" Width="300">
<StackPanel Width="auto" Margin="20px">
<ComboBox Width="100" SelectionChanged="ComboBox_Selected" x:Name="comboBox">
<ComboBoxItem>
<StackPanel Orientation="Horizontal">
<Rectangle Fill="Red" Height="20" Width="20"/>
<TextBlock Text=" Red"/>
</StackPanel>
</ComboBoxItem>
<ComboBoxItem>
<StackPanel Orientation="Horizontal">
<Rectangle Fill="Yellow" Height="20" Width="20"/>
<TextBlock Text=" Yellow"/>
</StackPanel>
</ComboBoxItem>
<ComboBoxItem>
<StackPanel Orientation="Horizontal">
<Rectangle Fill="Green" Height="20" Width="20"/>
<TextBlock Text=" Green"/>
</StackPanel>
</ComboBoxItem>
</ComboBox>
</StackPanel>
As you see, inside my ComboboxItems I have a rectangle and a textblock. Now I want to retreive the fill color of the rectangle (or the text of the textblock, it's the same) when my selectionchanged event is handled, so I can change the background of the window according to the selected color (which is the goal of the excercise).
To elaborate on my comment above, this is the Correct way to achieve what you need in WPF:
First of all, create a proper ViewModel that contains the list of available colors and a SelectedColor property:
public class ColorsViewModel
{
public ObservableCollection<string> Colors { get; private set; }
private string _selectedColor;
public string SelectedColor
{
get { return _selectedColor; }
set
{
_selectedColor = value;
MessageBox.Show("Selected Color: " + value); //message box here to show the code is actually working.
}
}
//... More code here in a moment
}
Then, make sure you populate the color collection with relevant data. In the case of colors specifically, WPF has built-in TypeConverters that can convert from (for example) string to System.Windows.Media.Color implicitly, therefore we can leverage that to simplify our code and use simple strings:
//Continuation of the above code
public ColorsViewModel()
{
Colors = new ObservableCollection<string>
{
"Red",
"Green",
"Blue",
"Yellow",
};
}
And finally create the UI in XAML using proper DataBinding:
<Window x:Class="WpfApplication3.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">
<ComboBox ItemsSource="{Binding Colors}"
SelectedItem="{Binding SelectedColor}"
VerticalAlignment="Center" HorizontalAlignment="Center">
<ComboBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<Rectangle Fill="{Binding}" Height="20" Width="20"/>
<TextBlock Text="{Binding}"/>
</StackPanel>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
</Window>
Result:
The change event is fired and the ComboBox.SelectedItem has the info you need.
You have to analyze the SelectedItem like my following method:
private void comboBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
ComboBoxItem comboBoxItem = this.comboBox.SelectedItem as ComboBoxItem;
if (comboBoxItem != null)
{
StackPanel stackPanel = comboBoxItem.Content as StackPanel;
if(stackPanel != null && stackPanel.Children[0] is Rectangle)
{
var fill = (stackPanel.Children[0] as Rectangle).Fill;
}
}
}
Here you get the fill of the rectangle and can handle this or do your stuff.
But be patient, this code is created exactly for you sample (ComboBoxItem with Content StackPanel with Children[0] as Rectangle). Changes will iterrupt the process ;)

Categories