I'm developing a UI with the WPF framework and MVVM.
This is the grid while loading:
image of the grid while displaying
This is the UI after the rendering has finished:
image of the grid when rendering is finished
So, as an example, the 3rd column isn't active and will collapse. But the user can see the whole building off the UI in the mainwindow.
Every view I've got is depending on the viewmodel. But also the controls got their own viewmodel. When I take my information together in one control, WPF is getting the information and draws the whole control, for example my tablecontrol. It´s a selfmade control, that is taking data and displays the data in a grid. Because it's one control, the whole grid is loaded in the background and displayed at once. When I make my own grid, because my tablecontrol can't handle the data, every control in the grid is displayed one after one and the grid is slowly displayed on the mainwindow.
<Grid HorizontalAlignment="Left">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="Auto"/>
.
.
.
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
.
.
.
</Grid.RowDefinitions>
<unitControls:Label Grid.Row="1" Grid.Column="0" Text="{Binding xx}"/>
<unitControls:Label Grid.Row="1" Grid.Column="1" Text="{Binding xx}"/>
<unitControls:Label Grid.Row="1" Grid.Column="2" Text="{Binding xx}"/>
.
.
.
<unitControls:Label Grid.Row="7" Grid.Column="7" Text="{Binding xx}"/>
<unitControls:Label Grid.Row="7" Grid.Column="8" Text="{Binding xx}"/>
<unitControls:Label Grid.Row="7" Grid.Column="9" Text="{Binding xx}"/>
<Grid>
Is there a way to display the whole content in one view at once? I tried to manipulate the dispatcher in the viewmodel, but nothing helped.
Try something like this:
XAML
<Window x:Class="Example.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">
<DockPanel>
<Button Content="Collect data" Click="OnCollectData" DockPanel.Dock="Top"/>
<ContentControl x:Name="HostControl"/>
</DockPanel>
</Window>
CS
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private async void OnCollectData(object sender, RoutedEventArgs e)
{
var data = await CollectDataAsync();
HostControl.Content = new MyCustomGridControl(data);
}
private async Task<GridDataHolder> CollectDataAsync()
{
GridDataHolder result = null;
await Task.Run(() =>
{
// Collect the logical data on the thread pool
result = GetGridData();
});
return result;
}
}
You can put the HostControl with a ProgressBar in a Grid and play with the Visibility property of the ProgressBar to improve the user experience.
Related
I want to use drawer layout in my app for hamburger but since my app have more than 30 pages having the drawer template declared on each page is not feasible hence I wanted to have the drawer layout as master page with a child/content page inside for which I have written below code with frame tag to navigate to page on click of menu in drawer layout.
Below is my XAML
<Grid x:Name="Rootlayout">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<!-- title bar-->
<Grid x:Name="TitleBar" Height="50" Grid.Row="0" Grid.Column="0" Grid.ColumnSpan="2" Background="SkyBlue">
<Image Source="/Assets/fs-logo.png" Height="50" HorizontalAlignment="Left" Margin="10,0,0,0"/>
</Grid>
<!--Drawer Layout-->
<drawer:DrawerLayout x:Name="DrawerLayout" Grid.Row="1" Grid.Column="0" Grid.ColumnSpan="2">
<Grid>
<Frame x:Name="contentFrame" />
</Grid>
<Grid x:Name="listFragment" FlowDirection="LeftToRight" >
<ListView x:Name="listItem" SelectionChanged="listItem_SelectionChanged">
<ListView.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding}" FontSize="20" Foreground="Black" Margin="10" VerticalAlignment="Center" HorizontalAlignment="Left"/>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</Grid>
</drawer:DrawerLayout>
</Grid>
While the behavior is exactly as I want but there is problem with hardware back button as when it's pressed the application gets exit. Even I have handle back button press event using following code is app.xaml.cs
//Below code of line in app constructor & it's method defination
#if WINDOWS_PHONE_APP
HardwareButtons.BackPressed += HardwareButtons_BackPressed;
#endif
#if WINDOWS_PHONE_APP
void HardwareButtons_BackPressed(object sender, Windows.Phone.UI.Input.BackPressedEventArgs e)
{
Frame rootFrame = Window.Current.Content as Frame;
if (frame != null && frame.CanGoBack)
{
frame.GoBack();
e.Handled = true;
}
}
#endif
Can anyone help me how to resolve this issue of back button or is there any other way i can utilize drawer layout by which I don't have to write the drawer layout xaml code on each page. One way is using usercontrol but since the content grid is part of drawerlayout I'm not able to figure out how to club my usercontrol & my normal page xaml. Please help thanx in advance
You are checking CanGoBack on the wrong frame.
The Window.Current.Content is your base frame containing the DrawerLayout. What you actually want to address is the contentFrame inside of it.
So what you really want to look at is:
(Window.Current.Content as RootPage)?.Frame.CanGoBack
Where RootPage is the name of your Page for the Xaml you posted in your question.
I'm trying to add a button to the title bar. My XAML looks like the following:
<Page
x:Class="FullScreen.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:FullScreen"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d">
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Grid x:Name="TitleBar">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="0" Text="Application Name" VerticalAlignment="Center"/>
<Button Grid.Column="1" Content="Test" Click="Button_Click"/>
</Grid>
</Grid>
<Grid Grid.Row="1">
<TextBlock>Content</TextBlock>
</Grid>
</Grid>
</Page>
The .cs page has this code:
using System;
using Windows.ApplicationModel.Core;
using Windows.UI.Popups;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
namespace FullScreen
{
public sealed partial class MainPage : Page
{
public MainPage()
{
InitializeComponent();
CoreApplication.GetCurrentView().TitleBar.ExtendViewIntoTitleBar = true;
Window.Current.SetTitleBar(TitleBar);
}
private async void Button_Click(object sender, RoutedEventArgs e)
{
await new MessageDialog("Click!").ShowAsync();
}
}
}
The button shows up but it doesn't respond to the click event. If I comment out the two lines in the constructor:
public MainPage()
{
InitializeComponent();
//CoreApplication.GetCurrentView().TitleBar.ExtendViewIntoTitleBar = true;
//Window.Current.SetTitleBar(TitleBar);
}
The button is in the main body of the app and the click event works correctly. What am I missing?
Thanx,
I'm trying to add a button to the title bar. The button shows up but it doesn't respond to the click event.
According to the official documentation(Remarks part of Window.SetTitleBar), this behavior is by design.
Input
When you call this method to set a XAML UIElement as the title bar, it lets Windows handle input to the title bar UIElement the same way it handles input to the default system title bar. For example, a user can move the window by dragging the XAML UIElement, or invoke the window context menu by right-clicking it.
This means that your app no longer receives pointer input when the user interacts with the target UIElement or its children using touch, mouse, or pen. However, you must still handle (or prevent) keyboard input, and determine whether content in the title bar can receive focus by tabbing to it with the keyboard.
In order to make the button within the title bar to respond to the click event, we can add a rectangle for the customizing title bar in xaml page first:
<Grid x:Name="TitleBar">
<!--Add a rectangle here-->
<Rectangle x:Name="BackgroundElement" />
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="0" Text="Application Name" VerticalAlignment="Center" />
<Button Grid.Column="1" Content="Test" Click="Button_Click" />
</Grid>
</Grid>
Then, set the title bar to the rectangle instead of the entire grid in code behind:
public MainPage()
{
InitializeComponent();
CoreApplication.GetCurrentView().TitleBar.ExtendViewIntoTitleBar = true;
// Set TitleBar to BackgroundElement instead of the entire grid
// Clicks on the BackgroundElement will be treated as clicks on the title bar.
Window.Current.SetTitleBar(BackgroundElement);
}
Here is the official Title bar sample for your reference, and following is the output for the test code above:
I am using a Grid to balance 3 parts. The first two shall take up each 50% of the remaining space, the last two shall stay the same height at the bottom, as it is a bar with buttons.
I use a GridSplitter to allow dynamic resizing between the first two elements, but removing it doesnt change anything either.
Here is a minimal working example:
XAML:
<Window x:Class="TestWPFApplication.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:TestWPFApplication"
mc:Ignorable="d"
Title="MainWindow" Height="350" Width="525">
<Grid x:Name="rootGrid">
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<TextBox
x:Name="firstBox"
Grid.Row="0"
AcceptsReturn="True"
AcceptsTab="True"
ScrollViewer.HorizontalScrollBarVisibility="Auto"
ScrollViewer.VerticalScrollBarVisibility="Auto"/>
<GridSplitter x:Name="splitter" Grid.Row="1" Height="5" HorizontalAlignment="Stretch" />
<TextBox
x:Name="secondBox"
Grid.Row="2"
AcceptsReturn="True"
AcceptsTab="True"
ScrollViewer.HorizontalScrollBarVisibility="Auto"
ScrollViewer.VerticalScrollBarVisibility="Auto"/>
<Button Grid.Row="3" Content="Collapse" Click="Button_Click"/>
</Grid>
</Window>
CS:
namespace TestWPFApplication
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void Button_Click(object sender, RoutedEventArgs e)
{
splitter.Visibility = Visibility.Collapsed;
secondBox.Visibility = Visibility.Collapsed;
}
}
}
After clicking the button, the second textbox and the gridsplitter collapse, so I cannot use them anymore (desired), but the remaining textbox does not take up the rest of the space (undesired).
In my understanding as the row has a star for height, it should automatically adjust its space. Do I need to call a function like updateUI or something so that the first textbox gets automatically resized?
You observe such behaviour, cause the heights of first and third grid row is set to '*'. After setting 2nd textBoxVisibility to collapsed 3rd grid row is not disappears. So if you write in Button_Click event handler something like this, all should work like expected.
rootGrid.RowDefinitions[2].MaxHeight = 0;
I can suggest to use DataBinding for it property to achieve this. You can bind second TextBox Visibility property to third RowDefinition MaxHeight property with ValueConverter. But I don't know the restrictions of your project.
I have a basic question. I have my MainPage.xaml code like this,
<!--LayoutRoot is the root grid where all page content is placed-->
<Grid x:Name="LayoutRoot" Background="White">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<!--TitlePanel contains the name of the application and page title-->
<StackPanel Grid.Row="0" Margin="12,17,0,28">
<TextBlock Text="{Binding Path=LocalizedResources.ApplicationTitle, Source={StaticResource LocalizedStrings}}"
Margin="9,-7,0,0" Style="{StaticResource PhoneTextTitle1Style}" Foreground="Black" />
</StackPanel>
<!--ContentPanel - place additional content here-->
<Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0" >
</Grid>
</Grid>
In my MainPage.xaml.cs file,
public MainPage()
{
InitializeComponent();
MessageBox.Show(ContentPanel.Height.ToString());
}
My message box returns NaN value but I would like to get a specific value rather than indefinite value.
Thanks.
you should check the actual height of your content panel in the Loaded event. In the constructor it will be NaN because the actual values are not yet determined...
Based on the suggestions,
public MainPage()
{
InitializeComponent();
this.Loaded += new RoutedEventHandler(DisplayMessage);
}
void DisplayMessage(object sender, RoutedEventArgs e)
{
MessageBox.Show(ContentPanel.ActualHeight.ToString());
}
This returns me height of my content panel.
Thanks.
I have a list of Games which just has an ID, a Date, and a Time.
I am setting this list as the DataContext.
I then have a DataTemplate for these games that is:
<DataTemplate DataType="{x:Type loc:Game}">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="auto"></RowDefinition>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="100"></ColumnDefinition>
<ColumnDefinition Width="100"></ColumnDefinition>
<ColumnDefinition Width="100"></ColumnDefinition>
</Grid.ColumnDefinitions>
<TextBlock Name="dateBlock" Grid.Column="0" Grid.Row="1"
Text="{Binding Date, StringFormat=d}"></TextBlock>
<TextBlock Name="TimeBlock" Grid.Column="1" Grid.Row="1"
Text="{Binding Time}"></TextBlock>
//need to but a button here for each row
</Grid>
</DataTemplate>
To use the template, I am simply just doing this:
<ListBox ItemsSource="{Binding}"></ListBox>
I need to add a Button to each line in this list view that have the same click event, but will somehow pass the ID of the game for which button is being clicked.
How can I do this? I am stuck.
If it doesn't make sense let me know and I will try to explain better.
For the first part, add a Button to the DataTemplate and subscribe to the Click event
<DataTemplate DataType="{x:Type loc:Game}">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="auto"></RowDefinition>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="100"></ColumnDefinition>
<ColumnDefinition Width="100"></ColumnDefinition>
<ColumnDefinition Width="100"></ColumnDefinition>
</Grid.ColumnDefinitions>
<TextBlock Name="dateBlock" Grid.Column="0" Grid.Row="1" Text="{Binding Date, StringFormat=d}"></TextBlock>
<TextBlock Name="TimeBlock" Grid.Column="1" Grid.Row="1" Text="{Binding Time}"></TextBlock>
<Button Click="Button_Click">X</Button>
</Grid>
</DataTemplate>
In the code behind event handler, you can get the DataContext of the clicked Button and find out the Id like
private void Button_Click(object sender, RoutedEventArgs e)
{
Button button = sender as Button;
Game game = button.DataContext as Game;
int id = game.ID;
// ...
}
Easily. Add a Button to your DataTemplate, give it a Command and then set the CommandParameter="{Binding}". The DataContext within a DataTemplate is the object.
As requested, some links to using commands.
WPF Commands Part 1: Basics
WPF Commands Part 2: Command Bindings and Gestures
MSDN Understanding Routed Events and Commands In WPF (ADVANCED)
HTH,
With a ListBox.ItemTemplate. Then in your click event you can get the object via DataContext.
<ListBox.ItemTemplate>
<DataTemplate>
<Button Content="^" IsEnabled="{Binding Path=IsNotFirst, Mode=OneWay}"
Click="btnMoveFDAup"/>
</DataTemplate>
</ListBox.ItemTemplate>
private void btnMoveFDAup(object sender, RoutedEventArgs e)
{
Button btn = ((Button)sender);
// btn.DataContext will get you to the row object where you can retrieve the ID
}