Hiding/Showing a UserControl WPF - c#

I am building a WPF MVVM application.
What I have:
I have a ShellWindow which looks like this:
It is composed by 2 rows:
1: the hamburger menu (not important) with Height="*"
2: the console with Height="100"
The console is a UserControl:
<UserControl
//namespaces>
<Grid Name="LoggingGrid" Background="Black">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<TextBlock Grid.Row="0" Margin="{StaticResource SmallLeftMargin}">
<Button
x:Name="CollapseBtn"
Width="25"
Height="25"
Click="CollapseBtn_Click"
Content="▲">
<Button.Template>
<ControlTemplate TargetType="{x:Type Button}">
<Grid>
<Ellipse Fill="White" />
<ContentPresenter
HorizontalAlignment="Center"
VerticalAlignment="Center"
Content="{TemplateBinding Content}" />
</Grid>
</ControlTemplate>
</Button.Template>
</Button>
<StackPanel Margin="5,0,0,0" Orientation="Horizontal">
<Image
Height="25"
Source="/Images/console-icon.png"
Visibility="Visible" />
<Label
Content="Console"
FontSize="16"
Foreground="White" />
</StackPanel>
</TextBlock>
<Border Grid.Row="1">
<ListView
x:Name="LoggingList"
Margin="5"
Background="Black"
BorderThickness="0"
Foreground="White"
ItemsSource="{Binding Logs, UpdateSourceTrigger=PropertyChanged}"
ScrollViewer.HorizontalScrollBarVisibility="Disabled"
ScrollViewer.VerticalScrollBarVisibility="Auto" />
</Border>
</Grid>
</UserControl>
I have omitted the non-important things.
What I want to do:
Whenever the user clicks on the button, the console should collapse and look something like this:
The arrow is also changed.
How can I implement this? What is the best approach using MVVM?
What I have tried:
I have tried using a button click event handler in the code behind - CollapseBtn_Click, just to see what will happen:
private void CollapseBtn_Click(object sender, System.Windows.RoutedEventArgs e)
{
LoggingGrid.Visibility = System.Windows.Visibility.Hidden;
}
Apparently it removes the user control and leaves a white background where it used to be.

Instead of setting the Visibility of the whole LoggingGrid to Hidden, you should set the Visibility of the LoggingList to Collapsed. (For the difference between Hidden and Collapsed, see here: Difference between Visibility.Collapsed and Visibility.Hidden).
Depending on your layout in the ShellWindow you probably have to adjust your row height configuration in the UserControl such that the collapsed LoggingGrid leads to a row with a height of zero.
Regarding MVVM the best approach would be to bind the Button to a bool property ConsoleVisible on your ViewModel such that clicking the button toggles the property between true and false. The styling of the button can be bound to the same property. For the LoggingList Visibility you could use a Binding with a BooleanToVisibilityConverter on the same property.

Related

WPF - Making a progressbar button and having it properly size

I am trying to make a button in WPF that will show progress but still have its button text visible.
I tried adding a Grid as a child to the button and adding a ProgressBar and Label to the grid, thinking the Grid will fill the button and using VerticalAlignment="Stretch" HorizontalAlignment="Stretch" on the progress bar and label will get me, basically, a clickable progress bar, that can show progress and have a label on top of it. However, I'm having problem with the sizing.
This is my XAML:
<Button Grid.Column="0" VerticalAlignment="Stretch" HorizontalAlignment="Stretch" MinWidth="100" MinHeight="25" Margin="3">
<Grid>
<ProgressBar Value="10" Maximum="20" VerticalAlignment="Stretch" HorizontalAlignment="Stretch"/>
<Label Content="asd2" VerticalAlignment="Stretch" HorizontalAlignment="Stretch" VerticalContentAlignment="Center" HorizontalContentAlignment="Center"/>
</Grid>
</Button>
This is what I see:
If I change to explicit sizing:
<Button Grid.Column="0" VerticalAlignment="Stretch" HorizontalAlignment="Stretch" MinWidth="100" MinHeight="25" Margin="3">
<Grid>
<ProgressBar Value="10" Maximum="20" Width="100" Height="30" VerticalAlignment="Stretch" HorizontalAlignment="Stretch"/>
<Label Content="asd2" Width="100" Height="30" VerticalAlignment="Stretch" HorizontalAlignment="Stretch" VerticalContentAlignment="Center" HorizontalContentAlignment="Center"/>
</Grid>
</Button>
I get this:
Which, visually, is an improvement, but XAML-programatically is worse, since I want the button to resize with the window, if the user can't see it well or something like that. Also, visually - I don't like that little border between the actual button and the start of the progressbar and I've tried setting both "padding" and "margin" to 0, it's not from that.
What I'd like to see - the progress bar taking up ALL the space of the button and the label text staying centered both vertically and horizontally, with respect to the total size of the button.
Put your <Grid> inside of a ControlTemplate and override your <Button.Template>:
<Button Grid.Column="0" VerticalAlignment="Stretch" HorizontalAlignment="Stretch" MinWidth="100" MinHeight="25" Margin="3">
<Button.Template>
<ControlTemplate TargetType="Button">
<Grid>
<ProgressBar Value="10" Maximum="20" VerticalAlignment="Stretch" HorizontalAlignment="Stretch"/>
<Label Content="asd2" VerticalAlignment="Stretch" HorizontalAlignment="Stretch" VerticalContentAlignment="Center" HorizontalContentAlignment="Center"/>
</Grid>
</ControlTemplate>
</Button.Template>
</Button>
What you were doing before is putting your <Grid> inside of the Button's ContentTemplate, not its Template. The problem with the ContentTemplate is that it has some of the default button's styling, such as the little border you don't like. By moving it to the Button's overall Template, you're saying I don't care about how the default button looks, I want it to look exactly like THIS.
What it can look like:
What it can look like if you resize the window:

ListView disable right tapped selection style

I am trying to disable the right click functionality of my items inside a list view. I have tried handling the RightTapped event and setting the e.handled value to true. This has covered most of the list view's area however if I right click on the borders of the list view item, this selection style is still applied. I want the user to still be able to use the left click and apply the list selected styling. It is just the right click I want to disable.
Here is what I have tried so far:
<ListView
ItemsSource="{Binding MyItems}"
ItemClick="OnItemClicked"
IsItemClickEnabled="True"
SelectionMode="Multiple"
CanDragItems="True"
DragItemsStarting="OnDragItemsStarting"
RightTapped="OnRightTapped"
IsRightTapEnabled="False">
<ListView.ItemContainerStyle>
<Style TargetType="ListViewItem">
<Setter Property="Margin" Value="-1,3,-1,0" />
<Setter Property="IsRightTapEnabled" Value="False"></Setter>
</Style>
</ListView.ItemContainerStyle>
<ListView.ItemTemplate>
<DataTemplate>
<Grid Background="Transparent" RightTapped="OnRightTapped" HorizontalAlignment="Left" Height="100" Width="300">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="100"></ColumnDefinition>
<ColumnDefinition Width="*"></ColumnDefinition>
</Grid.ColumnDefinitions>
<Image Grid.Column="0" Source="{Binding ImagePath}" Width="55" Height="55" Margin="0,0,0,1"/>
<Image Margin="20,0,0,22" Grid.Column="0" VerticalAlignment="Bottom" HorizontalAlignment="Left" Width="20" Height="20" Source="smallImage.png"/>
<StackPanel VerticalAlignment="Center" Margin="30,0,0,0" Grid.Column="1">
<TextBlock VerticalAlignment="Center" Text="{Binding Name}" Foreground="{Binding IsOnline, Converter={StaticResource BooleanToColorConverter}}" Style="{StaticResource BaseTextBlockStyle}" FontSize="20" Height="60"/>
</StackPanel>
</Grid>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
Here is the code for the RightTapped handler:
private void OnRightTapped(object sender, RightTappedRoutedEventArgs e)
{
e.Handled = true;
}
Edit: When I click on the borders of an Item in the ListView the OnRightTapped event is not being fired. What could be swallowing this event?
By playing with the background colors of the items inside my ListView.ItemTemplate, I found that my TextBlock was not covering the whole area of the row. By setting the Column width to the exact size of the ListView and setting the RightTapped event handler on this Grid I was able to cover all areas of right clicking. I still do not know why the RightTapped handler was not being fired for the ListView itself but this was a sufficient solution for what I needed.

WPF popup staysopen=false still keep the popup open while clicking outside

my problem here is I made a listbox inside the popup, and set the popup's staysopen=false. But each time popup box pops, I have to click something inside the popup(like select an element in listbox), then click outside the popup, and it will close automatically. If I don't click anything, and even if I click other elements outside the popup, the popup stays on. I need the popup closes without needing me to click any element inside it. What can I do? Here is the code, there are some other style link to this code but just some color style.
My control is when user click the textbox on the top of the popup box, the listbox pops. If user does nothing and click any place outside this element, the popup box closes. Thanks.
I can use the following code to get it done in silverlight. But seems like in wpf, it is not working anymore.
popupAncestor = FindHighestAncestor(this.ListBoxPopup);
if (popupAncestor == null)
{
return;
}
popupAncestor.AddHandler(System.Windows.Controls.Primitives.Popup.MouseLeftButtonDownEvent, (MouseButtonEventHandler)ClosePopup, true);
<Grid x:Name="MainGrid" Margin="0" VerticalAlignment="Center">
<Grid.RowDefinitions>
<RowDefinition Height="20"></RowDefinition>
<RowDefinition Height="auto"></RowDefinition>
</Grid.RowDefinitions>
<Grid Margin="1,1,1,0" x:Name="TopBar" Visibility="Visible" Grid.Row="0" Height="20" VerticalAlignment="Stretch" HorizontalAlignment="Stretch" Background="{StaticResource COL_BTN_LIGHT}">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"></ColumnDefinition>
<ColumnDefinition Width="19"></ColumnDefinition>
</Grid.ColumnDefinitions>
<TextBox x:Name="TextBoxSearchItem" x:FieldModifier="private" HorizontalAlignment="Stretch" Grid.Column="0" VerticalAlignment="Stretch" BorderThickness="0,0,0,0" Background="Transparent" TextChanged="TextBoxSearchItem_TextChanged"></TextBox>
<ToggleButton x:Name="DropDownArrorButton" Grid.Column="1" Style="{StaticResource ComboBoxReadonlyToggleButton}"></ToggleButton>
<!--<TextBlock HorizontalAlignment="Center" Text="Search" Grid.ColumnSpan="2" TextBlock.FontStyle="Italic" Opacity="0.4" VerticalAlignment="Center"/>-->
</Grid>
<Grid Grid.Row="1" HorizontalAlignment="Stretch" x:Name="PopupGrid" Margin="0,1,0,0" >
<Popup x:Name="ListBoxPopup" StaysOpen="False" x:FieldModifier="private" IsOpen="{Binding ElementName=DropDownArrorButton, Path=IsChecked, Mode=TwoWay}"
AllowsTransparency="true" Margin="0" HorizontalAlignment="Stretch" Placement="Bottom"
PlacementTarget="{Binding ElementName=TopBar}" Opened="OnPopupOpened" Closed="OnPopupClosed"
HorizontalOffset="{Binding ElementName=PopupGrid, Path=Value, Mode=TwoWay}"
VerticalOffset="{Binding ElementName=PopupGrid, Path=Value, Mode=TwoWay}">
<ListBox x:Name="ListBoxContainer" Width="{Binding ElementName=MainGrid, Path=ActualWidth}"
HorizontalContentAlignment="Stretch" SelectionMode="Single" Height="200" Margin="0"
SelectionChanged="ListBoxContainer_SelectionChanged"
MouseDoubleClick="ListBoxContainer_MouseDoubleClick">
<ListBox.ItemTemplate>
<DataTemplate>
<Grid HorizontalAlignment="Stretch">
<Border BorderBrush="{Binding SearchedBackColor}" BorderThickness="{Binding Indicator}" Width="{Binding ElementName=MainGrid, Path=ActualWidth}">
<TextBlock x:Name="ContentText" Text="{Binding Name}" Margin="1,0,0,0"/>
</Border>
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Popup>
<Border x:Name="listBorder" BorderBrush="{StaticResource COL_BTN}" BorderThickness="0,1,0,0" ></Border>
</Grid>
</Grid>
You should create a dependency property in your view model or control for "IsPopupOpen" as shown below to manage state of the popup. Then you can bind both the ToggleButton "IsChecked" and popup "IsOpen" to that DP.
Also on your ToggleButton, set "Focusable=false" and "IsThreeState=false"
public bool IsDropDownOpen
{
get { return (bool)GetValue(IsDropDownOpenProperty); }
set { SetValue(IsDropDownOpenProperty, value); }
}
public static readonly DependencyProperty IsDropDownOpenProperty =
DependencyProperty.Register("IsDropDownOpen", typeof(bool), typeof(Window), new UIPropertyMetadata(false));
Good luck!

Creating a button Template

I'm trying to create a template for a button that I can use over and over again on a form.
The Button, I want it to contain a Grid with two rows and a custom piece of text within the bottom row.
This is what I've got so far, but I don't think it's right because I want to set the text from within the button element.
<ControlTemplate TargetType="Control">
<Grid Width="444">
<Grid.RowDefinitions>
<RowDefinition Height="51" />
<RowDefinition Height="36" />
</Grid.RowDefinitions>
<Grid Grid.Row="0" Background="#286c97"></Grid>
<Grid Grid.Row="1" Background="#5898c0">
<TextBlock Grid.Row="1" FontFamily="Segoe UI" FontSize="12" Text="{TemplateBinding Content}" />
</Grid>
</Grid>
</ControlTemplate>
Then to call the template I was hoping I could go:
<Button Content="This is the text" />
But sadly this doesn't work. Is there some other template that I'm supposed to be using to pass the text value to it?
To make it work, there is a control called ContentPresenter. Place that inside your template wherever you want it to be. But remember, that it could be anything, a text, an image or a bunch of other controls, and your Button nor your ControlTemplate, should not care about what it is.
ControlTemplate TargetType="Control">
<Grid Width="444">
<Grid.RowDefinitions>
<RowDefinition Height="51" />
<RowDefinition Height="36" />
</Grid.RowDefinitions>
<Grid Grid.Row="0" Background="#286c97"></Grid>
<Grid Grid.Row="1" Background="#5898c0">
<ContentPresenter/>
</Grid>
</Grid>
</ControlTemplate>
The ContentPresenter, when used inside a ContentControl, like the button, automatically attaches to the Content, ContentTemplate and ContentTemplateSelector properties of the templated parent.
Now if you want to display more than just Text, or want to customize the text more, just pass a DataTemplate as your ContentTemplate directly to the specific button.
<DataTemplate x:Key="myButtonContentTemplate">
<TextBlock FontSize="18" Text="{Binding}"/>
</DataTemplate>
<Button ContentTemplate="{StaticResource myButtonContentTemplate}"/>

Silverlight, itemcontrol LostFocus and GetFocus not firing

Okay, so the situation as like this:
I've got an ItemsControl, which contains several children.
the children are actually a UserControl, this is it's Xaml:
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<!--DAY HEADER-->
<Border x:Name="dayHeader" Height="20" BorderBrush="#B0B6BE" BorderThickness="1" Grid.Row="0" Background="{StaticResource WeekHeader}">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="0" HorizontalAlignment="Left" VerticalAlignment="Center" TextAlignment="Center"
TextWrapping="NoWrap" Margin="1.5,0,0,0" Text="18"/>
<TextBlock Grid.Column="1" HorizontalAlignment="Center" VerticalAlignment="Center" TextAlignment="Center"
TextWrapping="NoWrap" Margin="2,0,0,0" Text="Thuesday" />
</Grid>
</Border>
<!--DAY HOURS-->
<ItemsControl x:Name="dayHours" Grid.Row="1">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Border Name="dayHourBorder" Height="30" BorderBrush="#B0B6BE" Width="193" Tag="{Binding Index}" BorderThickness="1,0,1,1" Background="AliceBlue"
MouseLeftButtonDown="dayHourBorder_MouseLeftButtonDown" MouseLeftButtonUp="dayHourBorder_MouseLeftButtonUp"
MouseMove="dayHourBorder_MouseMove" />
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</Grid>
IN SHORT
it's a grid that in the first row has a border
and in the second row has an ItemsControl.
Alright now... what i wanna do is, whenever i click between the child ItemControls (day hours) i want them to execute some function on the LostFocus() event and on GotFocus() event.
problem is... they don't fire! and it tried registering to them from every possible angle!
HALP.
UPDATE
I tried executing Focus() on MouseLeftButtonDown, but what happened is, it went straight to OnLostFocus, which is not what i want...
i don't understand it
Here is an overview on focus in Silverlight. The article mentions four conditions that need to be satisfied in order for the control to get focus. You should check those four conditions for your control and it should be fine I suppose.
You should also consider on which element you'd like to receive those events as GotFocus and LostFocus are bubbling events.
I've managed to fix this issue by doing this:
doing: this.Focus();
and then: e.Handled = true;
the problem was that the ItemControl usually can't hold focus, and so the click event bubbles up.
but when i tell him it's Handled, it stops it's bubbling and won't lose the focus.

Categories

Resources