It is my definition for xaml:
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Entry Placeholder="Write text..."/>
<Button Text="Button" Grid.Row="1" VerticalOptions="End" />
</Grid>
When I click on control Entry and keyboard is visable. My interface is rolled up:
I can fix it by remove all stars from RowDecinition Height but setting the value rigidly causes that it does not adapt to all screens.
Is it possible to turn off roll up interface when keyboard is visable?
You can try to set the Application.WindowSoftInputModeAdjust attached property to Pan.
Please refer to the following code:
<?xml version = "1.0" encoding = "UTF-8" ?>
<Application xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:MauiApp"
xmlns:android="clr-namespace:Microsoft.Maui.Controls.PlatformConfiguration.AndroidSpecific;assembly=Microsoft.Maui.Controls"
android:Application.WindowSoftInputModeAdjust="Pan"
x:Class="MauiApp0207.App">
<Application.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="Resources/Styles/Colors.xaml" />
<ResourceDictionary Source="Resources/Styles/Styles.xaml" />
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Application.Resources>
</Application>
For more information,you can check Soft keyboard input mode on Android.
Update
In IOS, you can try to obtain keyboard's frame via UIKeyboard.FrameEndUserInfoKey and calculate the height that need to change.
NSValue result = (NSValue)args.Notification.UserInfo.ObjectForKey(new NSString(UIKeyboard.FrameEndUserInfoKey));
CGSize keyboardSize = result.RectangleFValue.Size;
private void Entry_Focused(object sender, FocusEventArgs e)
{
if (DeviceInfo.Current.Platform == DevicePlatform.iOS)
{
NFloat bottom;
try
{
UIWindow window = UIApplication.SharedApplication.Delegate.GetWindow();
bottom = window.SafeAreaInsets.Bottom;
}
catch
{
bottom = 0;
}
var heightChange = (keyboardSize.Height - bottom);
layout.TranslateTo(0, originalTranslationY.Value - heightChange, 50);
}
}
private void Entry_Unfocused(object sender, FocusEventArgs e)
{
if (DeviceInfo.Current.Platform == DevicePlatform.iOS)
{
layout.TranslateTo(0, 0, 50);
}
}
Related
I have a grid with some ColumnDefinitions and RowDefinitions. What I like to do is drag a control at runtime and have it snap to a given GridColumn/GridRow when the control is over that GridColumn/GridRow. I was not able to find any resources on this. Perhaps I am using the wrong key words. Thanks in advance!
You should extend Grid to handle the drop position. Let the Grid add the dropped element to the appropriate cell.
The following simple but working example shows how to enable dragging of any UIElement from a Panel such as StackPanel or Grid to the custom DrockingGrid.
The custom Grid simply overrides the relevant drag&drop overrides. It's a minimal but working example, therefore only OnDragEnter and OnDrop are overridden.
On drop, you basically have to identify the cell the element was dropped in by using the drop position from the DragEventArgs. Then remove the dropped element from its original parent container (where the drag operation has started) and then insert it into the DockingGrid. You then use Grid.Row and Grid.Column to position the element in the appropriate cell:
DockingGrid.cs
public class DockingGrid : Grid
{
private bool AcceptsDrop { get; set; }
private Brush OriginalBackgroundBrush { get; set; }
public DockingGrid()
{
this.AllowDrop = true;
}
protected override void OnDragEnter(DragEventArgs e)
{
base.OnDragEnter(e);
e.Effects = DragDropEffects.None;
this.AcceptsDrop = e.Data.GetDataPresent(typeof(UIElement));
if (this.AcceptsDrop)
{
e.Effects = DragDropEffects.Move;
ShowDropTargetEffects();
}
}
protected override void OnDragLeave(DragEventArgs e)
{
base.OnDragEnter(e);
ClearDropTargetEffects();
}
protected override void OnDrop(DragEventArgs e)
{
base.OnDrop(e);
if (!this.AcceptsDrop)
{
return;
}
ClearDropTargetEffects();
var droppedElement = e.Data.GetData(typeof(UIElement)) as UIElement;
RemoveDroppedElementFromDragSourceContainer(droppedElement);
_ = this.Children.Add(droppedElement);
Point dropPosition = e.GetPosition(this);
SetColumn(droppedElement, dropPosition.X);
SetRow(droppedElement, dropPosition.Y);
}
private void SetRow(UIElement? droppedElement, double verticalOffset)
{
double totalRowHeight = 0;
int targetRowIndex = 0;
foreach (RowDefinition? rowDefinition in this.RowDefinitions)
{
totalRowHeight += rowDefinition.ActualHeight;
if (totalRowHeight >= verticalOffset)
{
Grid.SetRow(droppedElement, targetRowIndex);
break;
}
targetRowIndex++;
}
}
private void SetColumn(UIElement? droppedElement, double horizontalOffset)
{
double totalColumnWidth = 0;
int targetColumntIndex = 0;
foreach (ColumnDefinition? columnDefinition in this.ColumnDefinitions)
{
totalColumnWidth += columnDefinition.ActualWidth;
if (totalColumnWidth >= horizontalOffset)
{
Grid.SetColumn(droppedElement, targetColumntIndex);
break;
}
targetColumntIndex++;
}
}
private void RemoveDroppedElementFromSourceContainer(UIElement droppedElement)
{
DependencyObject parent = droppedElement is FrameworkElement frameworkElement
? frameworkElement.Parent
: VisualTreeHelper.GetParent(droppedElement);
if (parent is null)
{
return;
}
switch (parent)
{
case Panel panel:
panel.Children.Remove(droppedElement);
break;
case ContentControl contentControl:
contentControl.Content = null;
break;
case ContentPresenter contentPresenter:
contentPresenter.Content = null;
droppedElement.UpdateLayout();
break;
case Decorator decorator:
decorator.Child = null;
break;
default:
throw new NotSupportedException($"Parent type {parent.GetType()} not supported");
}
}
private void ShowDropTargetEffects()
{
this.ShowGridLines = true;
this.OriginalBackgroundBrush = this.Background;
this.Background = Brushes.LightBlue;
}
private void ClearDropTargetEffects()
{
this.Background = this.OriginalBackgroundBrush;
this.ShowGridLines = false;
}
}
Usage
Use it like a normal Grid.
Now the user can drag any control into any of the predefined cells.
<local:DockingGrid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="100" />
<ColumnDefinition Width="200" />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="100" />
<RowDefinition Height="300" />
<RowDefinition />
</Grid.RowDefinitions>
</local:DockingGrid>
In the parent host of the drag&drop context for example the Window, enable/start the drag behavior:
MainWindow.xaml.cs
partial class MainWindow : Window
{
protected override void OnPreviewMouseMove(MouseEventArgs e)
{
base.OnPreviewMouseMove(e);
if (e.LeftButton == MouseButtonState.Pressed
&& e.Source is UIElement uIElement)
{
_ = DragDrop.DoDragDrop(uIElement, new DataObject(typeof(UIElement), uIElement), DragDropEffects.Move);
}
}
}
See Microsoft Docs: Drag and Drop Overview to learn more about the feature.
The short answer is to put that control inside something which fills that cell. You could just put it in that grid cell by adding it to the grid children and setting grid row and column attached properties but there is a gotcha.
A grid cell is sort of conceptual.
The grid looks at it's content, looks at it's definitions for rows and columns and works out where to put it's content using measure arrange passes.
Which is a wordy way of saying there's nothing there to drag your control into.
You need a drop target to drag drop anything into. As it's name suggests, you need some sort of a receptacle for the thing you are dragging.
Wpf, however has these things called content controls.
A button actually inherits from content control to allow it to have things like a string in it.
There is also a content control itself. Which is just kind of like a receptacle for something or other.
One of these things can be used in a given cell as a sort of a place holder. And then you have something in a cell that you can drop into.
I think if you just throw a contentcontrol in a grid without anything inside it you might have problems hit testing.
Some experimentation in a scratch project is advisable.
But basically you could have something like:
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Rectangle Fill="Red"
Name="DraggAbleThing"
MouseMove="DraggAbleThing_MouseMove"
/>
<ContentControl Grid.Row="1"
Grid.Column="1"
x:Name="BottomRight"
AllowDrop="True"
>
<Rectangle Fill="Yellow"/>
</ContentControl>
</Grid>
There's a fair bit to implement in order to do drag drop but the idea here is you have something in the bottom right cell which you can drop into. You might have to set ishitestable=false on that yellow rectangle.
I'd have to implement all the drag drop methods to try it out.
If I did and drop works ok then when the contentcontrol gets draggablething dropped into it.
Set the content property of the contentcontrol to draggablething and it is now in the bottom right cell.
It will fill that cell because the grid arranges it's contents to fill whichever logical cell it decides they're "in".
I would like to present an example I wrote that is working.
In the Application I wrote I have a Grid with 4 Rows and 4 Columns.
I can place in each Cell a different UserControl that is based on a class I called
BaseDragDropUserControl:
public class BaseDragDropUserControl: UserControl
{
protected override void OnMouseMove(MouseEventArgs e)
{
base.OnMouseMove(e);
if (e.LeftButton == MouseButtonState.Pressed)
{
DataObject data = new DataObject();
data.SetData(DataFormats.StringFormat, nameof(BaseDragDropUserControl));
BaseDragDropUserControl tobemMoved = (BaseDragDropUserControl)e.Source;
int row = (int)tobemMoved.GetValue(Grid.RowProperty);
int col = (int)tobemMoved.GetValue(Grid.ColumnProperty);
data.SetData("Source", tobemMoved);
data.SetData("Row", row);
data.SetData("Col", col);
DragDrop.DoDragDrop(this, data, DragDropEffects.Move);
}
}
protected override void OnGiveFeedback(GiveFeedbackEventArgs e)
{
base.OnGiveFeedback(e);
if (e.Effects.HasFlag(DragDropEffects.Copy))
{
Mouse.SetCursor(Cursors.Cross);
}
else if (e.Effects.HasFlag(DragDropEffects.Move))
{
Mouse.SetCursor(Cursors.Pen);
}
else
{
Mouse.SetCursor(Cursors.No);
}
e.Handled = true;
}
protected override void OnDrop(DragEventArgs e)
{
base.OnDrop(e);
// If the DataObject contains string data, extract it.
if (e.Data.GetDataPresent(DataFormats.StringFormat))
{
string dataString = (string)e.Data.GetData(DataFormats.StringFormat);
if (dataString == nameof(BaseDragDropUserControl))
{
int targetRow = (int)this.GetValue(Grid.RowProperty);
int targetCol = (int)this.GetValue(Grid.ColumnProperty);
int originRow = (int)e.Data.GetData("Row");
int originCol = (int)e.Data.GetData("Col");
BaseDragDropUserControl origin = (BaseDragDropUserControl)e.Data.GetData("Source");
this.SetValue(Grid.RowProperty, originRow);
this.SetValue(Grid.ColumnProperty, originCol);
origin.SetValue(Grid.RowProperty, targetRow);
origin.SetValue(Grid.ColumnProperty, targetCol);
}
}
e.Handled = true;
}
}
The above class is the "Heavy one". It handle both the Drag and the Drop functions.
It ships data object with the origin UserControl and also intercept it when it is dropped. It switch the Grid.Row and Grid.Column values between the origin UserControl and the Target UserControl. In doing this the locations are changed.
I created 2 UsserControls.
RedUserControl and BlueUserControl:
<local:BaseDragDropUserControl x:Class="Problem10.RedUserControl"
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:Problem10"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800" AllowDrop="True">
<Grid>
<Rectangle Fill="Red"/>
</Grid>
</local:BaseDragDropUserControl>
<local:BaseDragDropUserControl x:Class="Problem10.BlueUserControl"
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:Problem10"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800" AllowDrop="True">
<Grid>
<Rectangle Fill="Blue"/>
</Grid>
</local:BaseDragDropUserControl>
The MainWindow is as following:
<Grid >
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition/>
<ColumnDefinition/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition/>
<RowDefinition/>
<RowDefinition/>
</Grid.RowDefinitions>
<local:RedUserControl Grid.Row="0" Grid.Column="1"/>
<local:BlueUserControl Grid.Row="0" Grid.Column="0"/>
<local:BlueUserControl Grid.Row="0" Grid.Column="2"/>
<local:BlueUserControl Grid.Row="0" Grid.Column="3"/>
<local:RedUserControl Grid.Row="1" Grid.Column="0"/>
<local:BlueUserControl Grid.Row="1" Grid.Column="1"/>
<local:BlueUserControl Grid.Row="1" Grid.Column="2"/>
<local:BlueUserControl Grid.Row="1" Grid.Column="3"/>
<local:RedUserControl Grid.Row="2" Grid.Column="0"/>
<local:BlueUserControl Grid.Row="2" Grid.Column="1"/>
<local:BlueUserControl Grid.Row="2" Grid.Column="2"/>
<local:BlueUserControl Grid.Row="2" Grid.Column="3"/>
<local:RedUserControl Grid.Row="3" Grid.Column="0"/>
<local:BlueUserControl Grid.Row="3" Grid.Column="1"/>
<local:BlueUserControl Grid.Row="3" Grid.Column="2"/>
<local:BlueUserControl Grid.Row="3" Grid.Column="3"/>
</Grid>
</Window>
The Application is ready for you ! Come and play.
I am trying to find this out for days already without luck,
what i want to achieve is to add flowdocument xaml code from string.
String like:
string test = "<Paragraph><Run>Text</Run></Paragraph>";
But i want to add it not at the end of the block or document, but at current caret position.
I need it to be able to enable copying of UIElements across my RichTextbox.
Thanks for help !
The XAML text like <Paragraph><Run>Text</Run></Paragraph> can't be inserted to the FlowDocument directly.
It should be converted to proper Flow Related Classes.
In your case create the Paragraph object and insert it on the current caret position:
The MainWindows.xaml:
<Window x:Class="WpfApp12.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"
mc:Ignorable="d"
Topmost="True"
Title="MainWindow" Height="350" Width="400">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<ToolBar Margin="0,0,0,0">
<Menu VerticalAlignment="Center" Background="Transparent">
<MenuItem Header="Code Block" Click="InsertBlock_Click"/>
</Menu>
</ToolBar>
<Grid Grid.Row="1">
<RichTextBox x:Name="rtb" Margin="5" IsDocumentEnabled="True">
<FlowDocument>
<Paragraph Margin="0" Padding="0" FontSize="14" FontWeight="Bold">RichTextBox</Paragraph>
<Paragraph>A RichTextBox is a better choice when it is necessary for the user to edit formatted text, images, tables, or other rich content.</Paragraph>
</FlowDocument>
</RichTextBox>
</Grid>
</Grid>
</Window>
The MainWindow.xaml.cs:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void InsertBlock_Click(object sender, RoutedEventArgs e)
{
// <Paragraph><Run>Text</Run></Paragraph>
var paragraph = new Paragraph();
paragraph.Inlines.Add(new Run("Text"));
if (rtb.CaretPosition.IsEndOfBlock())
{
rtb.Document.Blocks.InsertAfter(rtb.CaretPosition.Paragraph, paragraph);
}
else
{
if (rtb.CaretPosition.IsAtLineStartPosition)
{
rtb.Document.Blocks.InsertBefore(rtb.CaretPosition.Paragraph, paragraph);
}
else
{
rtb.Document.Blocks.InsertBefore(rtb.CaretPosition.InsertParagraphBreak().Paragraph, paragraph);
}
}
rtb.Focus();
}
// Implementation of the `IsEndOfBlock()` extension method
public static class TextRangeExt
{
public static bool IsEndOfBlock(this TextPointer position)
{
for (; position != null; position = position.GetNextContextPosition(LogicalDirection.Forward))
{
switch (position.GetPointerContext(LogicalDirection.Forward))
{
case TextPointerContext.ElementEnd:
if (position.GetAdjacentElement(LogicalDirection.Forward) is Paragraph) return true;
break;
default:
return false;
}
}
return false;
}
}
}
So I have a button in my AppBar that adds buttons to a stackpanel that is located in a grid on the page.
I assigned a RightTapped event to the new buttons.
However, when I right click a new button, instead of firing the method I assigned to the RightTapped event, the program inflates the AppBar.
I tried to set IsRightTapEnabled="False" one every item but the new buttons, but that didn't help the issue.
I'm stuck and I need help.
Here is my code behind:
public sealed partial class MainPage : Page
{
public MainPage()
{
this.InitializeComponent();
}
int index = 0;
private void AppBarButton_Click(object sender, RoutedEventArgs e)
{
index++;
string ButtonName = "Button" + index;
Button dummyButton = new Button
{
Name = ButtonName,
Content = ButtonName,
};
StackPanel1.Children.Add(dummyButton);
dummyButton.RightTapped += new RightTappedEventHandler(DummyButton_RightTapped);
}
private void Button0_RightTapped(object sender, RightTappedRoutedEventArgs e)
{
MenuFlyout myFlyout = new MenuFlyout();
MenuFlyoutItem firstItem = new MenuFlyoutItem { Text = "Right Clicked" };
myFlyout.Items.Add(firstItem);
myFlyout.ShowAt(sender as FrameworkElement);
}
private void DummyButton_RightTapped(object sender, RightTappedRoutedEventArgs e)
{
//var dialog = new MessageDialog("Right clicked");
//await dialog.ShowAsync();
MenuFlyout myFlyout = new MenuFlyout();
MenuFlyoutItem firstItem = new MenuFlyoutItem { Text = "Right Clicked" };
myFlyout.Items.Add(firstItem);
myFlyout.ShowAt(sender as FrameworkElement);
}
}
Here is my XAML code:
<Page
x:Class="Soundboard.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:Soundboard"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
IsRightTapEnabled="False">
<Page.TopAppBar >
<AppBar IsSticky="True" IsRightTapEnabled="False" >
<StackPanel HorizontalAlignment="Right" Orientation="Horizontal">
<AppBarButton Label="Add Sound" Icon="OpenFile" Click="AppBarButton_Click" ></AppBarButton>
</StackPanel>
</AppBar>
</Page.TopAppBar>
<Grid Background="#FF004D40" Name="myGrid" IsRightTapEnabled="False">
<Grid.RowDefinitions>
<RowDefinition Height="1*"/>
<RowDefinition Height="1*"/>
<RowDefinition Height="1*"/>
<RowDefinition Height="1*"/>
<RowDefinition Height="1*"/>
<RowDefinition Height="1*"/>
</Grid.RowDefinitions>
<StackPanel Name="StackPanel1" Grid.Row="0" Orientation="Horizontal" IsRightTapEnabled="False">
<Button Content="Button0" Name ="Button0" RightTapped="Button0_RightTapped"></Button>
</StackPanel>
</Grid>
The official does not recommend using the AppBar in UWP. The following section references Official AppBar instructions.
You should use the AppBar only when you are upgrading a Universal Windows 8 app that uses the AppBar, and need to minimize changes. For new apps in Windows 10, we recommend using the CommandBar control instead.
Usage
Add CommandBar to <Page.TopAppBar> just like following code.
<Page.TopAppBar>
<CommandBar IsSticky="True">
<AppBarButton
Click="AppBarButton_Click"
Icon="OpenFile"
Label="Add Sound" />
</CommandBar>
</Page.TopAppBar>
I'm trying to build an app that displays in a Pivot informations about several products such as their pictures. Each PivotItem is concerning one product and contains (between other controls) another Pivot where I load the pictures of the product in code behind.
Here's the XAML part :
<Page
x:Class="Inventaire.Fenetres.FicheProduit"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:Inventaire"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<Page.Resources>
<ResourceDictionary>
<DataTemplate x:Key="ProductPivotItem">
<Grid x:Name="rootGrid" Loaded="rootGrid_Loaded">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<TextBlock x:Name="productName" Text="{Binding article.name}" FontSize="18"
FontWeight="Bold" HorizontalAlignment="Center" TextWrapping="Wrap"
Grid.Row="0" Grid.Column="0" Grid.ColumnSpan="2"/>
<Pivot x:Name="picturesPivot" HorizontalAlignment="Center" Margin="0,5,0,5"
VerticalContentAlignment="Top" Grid.Row="1" Grid.Column="0" Grid.ColumnSpan="2"/>
<!--
Some other controls
-->
</Grid>
</DataTemplate>
</ResourceDictionary>
</Page.Resources>
<Pivot x:Name="productPivot" ItemsSource="{Binding}"
ItemTemplate="{StaticResource ProductPivotItem}" />
</Page>
For the moment I load images in the rootGrid_Loaded event, using VisualTreeHelper to get picturesPivot with a method i found there.
Here's the C# extracts :
private void rootGrid_Loaded(object sender, RoutedEventArgs e)
{
Grid rootGrid = (Grid)sender;
// FindArticle is a method I wrote to get the product (of class Article) concerned by
// the productPivotItem, according to me irrelevant for my problem
Article art = FindArticle(rootGrid);
Pivot picturesPivot = (Pivot)FindChildControl<Pivot>(rootGrid, "picturesPivot");
loadImage(picturesPivot, art);
}
private async void loadImage(Pivot picturesPivot, Article article)
{
for (int i = 0 ; i < article.images.Count ; i++)
{
// ImageProduit is the class gathering infomations I need to build the picture url
// the images property of the Article class is the collection of ImageProduit for the product
// and I use AppelWebService (pattern singleton) to get the image from web
ImageProduit picture = article.images[i];
BitmapImage bmpImage = await AppelWebService.getInstance().loadImage(picture.ToString());
Image img = new Image();
img.Source = bmpImage;
PivotItem pi = new PivotItem();
pi.Content = img;
picturesPivot.Items.Add(pi);
}
}
private DependencyObject FindChildControl<T>(DependencyObject control, string ctrlName)
{
DependencyObject result = null;
bool done = false;
int i = 0;
int childNumber = VisualTreeHelper.GetChildrenCount(control);
while (i < childNumber && !done)
{
DependencyObject child = VisualTreeHelper.GetChild(control, i);
FrameworkElement fe = child as FrameworkElement;
if (fe == null)
{
done = true;
}
else if (child is T && fe.Name == ctrlName)
{
result = child;
done = true;
}
else
{
DependencyObject nextLevel = FindChildControl<T>(child, ctrlName);
if (nextLevel != null)
{
result = nextLevel;
done = true;
}
}
i++;
}
return result;
}
I modified a bit the FindChildControl method in order to have only one return at the end of the method.
Wrote like this I have no problems loading images.
But, sliding on many products, i discover that after around 70 productPivotItem loaded my emulator crash for OutOfMemoryException.
So I want to try to clear picturesPivot.Items when leaving the corresponding productPivotItem to see if it solve the memory problem.
For this I thought use the PivotItemLoaded and PivotItemUnloaded events on productPivot, load images on load and clear the picturesPivot items collection on unload.
Unfortunately I am not able to get back the picturesPivot in these event methods.
Here's what I tried :
private void productPivot_PivotItemLoaded(Pivot sender, PivotItemEventArgs args)
{
// Next three lines independently
args.Item.UpdateLayout();
sender.UpdateLayout();
UpdateLayout();
Pivot picturesPivot = (Pivot)FindChildControl<Pivot>(args.Item, "picturesPivot");
}
Debuging step by step I saw that args.Item has one child, a Grid without name that has himself one child, a ContentPresenter. This ContentPresenter has no child and I can't get any of my controls defined in the DataTemplate.
How could I find them ? I really need you as I had tearing out on this for too long. I hope I was clear enough, the Pivot Inside Pivot thing can be confusing.
I try to make the button move when I press an arrow key on the keyboard.
But what I get is that I always need to press the button with mouse to get the right focus first, and then I can move it with the left arrow key, otherwise not. However, as what I know, the KeyDown event is triggered by the Grid instead of the button.
Here is how I do it in the Code behind:
private void Panel_KeyDown(object sender, KeyEventArgs e)
{
Button source = Baffle;
if (source != null)
{
if (e.Key == Key.Left)
{
source.Margin = new Thickness(source.Margin.Left - 1, source.Margin.Top,
source.Margin.Right + 1, source.Margin.Bottom);
}
}
}
The XAML:
<Grid Name="Panel" KeyDown="Panel_KeyDown" Background="BlanchedAlmond">
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Button Name="Baffle" Template="{StaticResource ButtonTemplate}"
Grid.Row="1" VerticalAlignment="Bottom" Margin="20" HorizontalAlignment="Center"
Width="50" Height="20"/>
</Grid>
Could anyone explain this? Thanks.
Interesting ... Not sure why, but if you want to solve it in a simple way you can use this:
public partial class MainWindow : Window
{
private Button source;
public MainWindow()
{
InitializeComponent();
source = Baffle;
source.Focus();
}
private void Panel_KeyDown(object sender, KeyEventArgs e)
{
if (source != null)
{
if (e.Key == Key.Left)
{
source.Margin = new Thickness(source.Margin.Left - 1, source.Margin.Top,
source.Margin.Right + 1, source.Margin.Bottom);
}
}
}
}
(simply give that button the focus on load,and you can move it to your heart content).
That's right - your KEYDOWN event fires only when Grid(Panel) has focus on it. But when your application starts it hasn't focus on it and will get it only when you select any control on Grid for example this button or another one. MainWindow has focus on start so just add this event handler to MainWindow KeyDown.
<Window x:Class="WpfApplication4.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" KeyDown="Panel_KeyDown">
<Grid Name="Panel" Background="BlanchedAlmond">
.....
This is because Grid by default is not focusable, so the KeyEvent is not going to work until the Grid has focus or one of the controls in the Grid FocusScope has logical focus.
You can set the Grid to Focusable and set the FocusedElement using FocusManager to the Grid and this will work
Example:
<Grid Name="Panel" KeyDown="Panel_KeyDown" Background="BlanchedAlmond" FocusManager.FocusedElement="{Binding ElementName=Panel}" Focusable="True">
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Button Name="Baffle"
Grid.Row="1" VerticalAlignment="Bottom" Margin="20" HorizontalAlignment="Center"
Width="50" Height="20"/>
</Grid>