How do you get Canvas.SetTop to work on ItemsCollection items? - c#
Below is the complete program (XAML and code behind). When started you will see:
When you click [Show/Hide] you see:
When you uncheck Home and Documents and click [Apply] you see:
As you can see, the Home and Documents folder disappear, but the other folders do not move up. Looking at the two list boxes you see three items per line, folder label, the value for Canvas.SetTop, and True: Visibility.Visible or False: Visibility.Collapsed. The Original Placement list box are the original values. Each time you click [Apply] the New Placement List box shows that new values. The Visibility value gets applied and the result is visible, either the folder is there or it is not. However, applying the Canvas.SetTop does not work. And you can see that the values have changed for the Canvas.SetTop. And, if you look at the code, you see that it is applied to the same UIElement as the Visibility is applied to:
var f = Me[i];
Canvas.SetTop(iec, f.Top); // Why does this not work and
iec.Visibility = f.FolderVisible; // this does work?
I need the folders that are visible to move up to take the place of those that are collapsed. Canvas.SetTop is not working. How can I get the Folders to move?
Here is the Solution Explorer pane so that you know what is needed:
Here is the XAML:
<Window x:Class="FolderTest.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:system="clr-namespace:System;assembly=mscorlib"
xmlns:local="clr-namespace:FolderTest"
Title="Folder Test"
Width="450"
Height="410">
<Grid Margin="10">
<StackPanel Orientation="Horizontal">
<StackPanel Orientation="Vertical">
<!-- Folder Show/Hide Button -->
<Button Name="FolderOptions" Content="Show/Hide" FontSize="8" FontWeight="Bold" Width="50" Height="20" Margin="116, 0, 0, 0" Click="Event_ShowHide_ButtonClick" />
<!-- Folders -->
<Grid>
<Canvas Name="xFolders"
Width="170"
Height="309"
VerticalAlignment="Top"
HorizontalAlignment="Left"
Margin="0,10,0,0">
<Canvas.Resources>
<local:Folders x:Key="myFolders" />
</Canvas.Resources>
<ItemsControl ItemsSource="{Binding Source={StaticResource myFolders}}">
<ItemsControl.Template>
<ControlTemplate>
<ItemsPresenter />
</ControlTemplate>
</ItemsControl.Template>
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<Canvas />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<DataTemplate.Resources>
</DataTemplate.Resources>
<Canvas Name="FolderCanvas"
Tag="{Binding Path=Number}"
Visibility="{Binding Path=FolderVisible}">
<Path Data="{Binding Path=FolderPath}"
Stroke="{Binding Path=Brush}"
Fill="{Binding Path=Brush}"/>
<StackPanel Orientation="Horizontal"
Canvas.Left="5"
Canvas.Top="2">
<TextBlock Text="{Binding Path=Label}"
FontSize="12"
Margin="20, 0, 0, 0" />
</StackPanel>
</Canvas>
</DataTemplate>
</ItemsControl.ItemTemplate>
<ItemsControl.ItemContainerStyle>
<Style>
<Setter Property="Canvas.Top"
Value="{Binding Top}" />
</Style>
</ItemsControl.ItemContainerStyle>
</ItemsControl>
</Canvas>
</Grid>
</StackPanel>
<StackPanel Orientation="Vertical" Margin="10, 0, 0, 0">
<TextBlock Text="Original Placemet:" />
<ListBox Name="OriginalPlacement" />
<Button Name="Refresh" Content="New Placemet:" Margin="0, 10, 0, 0" Click="Event_OriginalPlacement_ButtonClick" />
<ListBox Name="NewPlacement" />
</StackPanel>
</StackPanel>
</Grid>
</Window>
Here is the C# Code Behind:
using System;
using System.Collections.Generic;
using System.Windows.Media;
using System.Windows.Controls;
using System.Windows;
using System.Collections.ObjectModel;
using System.Windows.Controls.Primitives;
using System.Windows.Input;
namespace FolderTest
{
public struct GD
{
public static MainWindow MainWindow = null;
public static Button Refresh = null;
}
public partial class MainWindow : Window
{
public MainWindow()
{
GD.MainWindow = this;
InitializeComponent();
Loaded += new RoutedEventHandler(MainWindow_Loaded);
}
void MainWindow_Loaded(object sender, RoutedEventArgs e)
{
GD.Refresh = Refresh;
Folders.Placement(ref OriginalPlacement);
}
private void Event_ShowHide_ButtonClick(object sender, RoutedEventArgs e)
{
var pu = Folders.FolderShowPopUp();
pu.PlacementTarget = FolderOptions;
pu.IsOpen = true;
}
private void Event_OriginalPlacement_ButtonClick(object sender, RoutedEventArgs e)
{
Folders.Placement(ref NewPlacement);
}
}
public class Folder
{// Specification for each folder tab seen on the left.
public int Number { get; set; } // Folder sequence number top to bottom starging with 1.
public string Label { get; set; } // The label that appears on the folder tab itself.
public double Top { get; set; } // Position of the upper left corner of this folder.
public SolidColorBrush Brush { get; set; } // Solid Color Brush.
public string FolderPath { get; set; } // Contains the geometric path to draw this folder and its tab.
public Visibility FolderVisible { get; set; } // This folder's Show/Hide flag.
public Folder(int Number, string Label, double Top, SolidColorBrush Brush, string FolderPath, Visibility FolderVisible)
{
this.Number = Number;
this.Label = Label;
this.Top = Top;
this.Brush = Brush;
this.FolderPath = FolderPath;
this.FolderVisible = FolderVisible;
}
}
public class Folders : ObservableCollection<Folder>
{
public static ObservableCollection<Folder> Me = null;
private static string[] Labels = new string[]
{
"Personal",
"Health",
"Finances",
"Home",
"Employment",
"Insurance",
"Documents",
"Contacts",
"Journal"
};
private static Dictionary<string, Tuple<bool, bool>> LabelData = new Dictionary<string, Tuple<bool, bool>>()
{// Label Show Hidable
{Labels[0], new Tuple<bool, bool>(true, false)},
{Labels[1], new Tuple<bool, bool>(true, true)},
{Labels[2], new Tuple<bool, bool>(true, true)},
{Labels[3], new Tuple<bool, bool>(true, true)},
{Labels[4], new Tuple<bool, bool>(true, true)},
{Labels[5], new Tuple<bool, bool>(true, true)},
{Labels[6], new Tuple<bool, bool>(true, true)},
{Labels[7], new Tuple<bool, bool>(true, false)},
{Labels[8], new Tuple<bool, bool>(true, true)}
};
private static string[] FolderColors = new string[]
{
"FF36579E",
"FFDF2024",
"FF16A146",
"FF00B2D4",
"FFF47B20",
"FF9F1F63",
"FF13A89E",
"FFB7B7E7",
"FF50CAF5",
"FFAA9E74",
"FF86787D",
"FF36D146",
};
private static byte[] ARGBColor = new byte[4]; // Byte array for the folder top color.
private static void ColorString2ARGB(ref byte[] bytes, string Hex)
{// Converts 8 char hex string to 4 byte array.
for (int i = 0; i < 8; i += 2)
bytes[i / 2] = Convert.ToByte(Hex.Substring(i, 2), 16);
}
private static int colorIndex = -1; // Initial value of the colorIndex.
private static string NextColor()
{// Returns a 8 char string containing the next top FolderColor. If at end, cycle to beginning.
colorIndex++;
if (colorIndex >= FolderColors.Length) colorIndex = 0;
return FolderColors[colorIndex];
}
private class FolderShow
{
public string Label { get; set; }
public bool Show { get; set; }
public bool Hidable { get; set; }
public FolderShow(string label, bool show, bool hidable)
{
Label = label;
Show = show;
Hidable = hidable;
}
}
private static List<FolderShow> FolderShowList = null;
public static Popup FolderShowPopUp()
{
var pu = new Popup()
{
Placement = PlacementMode.Right,
AllowsTransparency = true,
StaysOpen = false
};
var b = new Border()
{
BorderThickness = new Thickness(2),
BorderBrush = new SolidColorBrush() { Color = Colors.Black },
CornerRadius = new CornerRadius(5),
Background = new SolidColorBrush() { Color = Colors.White }
};
var sp = new StackPanel()
{
Orientation = Orientation.Vertical,
Margin = new Thickness(10)
};
var tb = new TextBlock()
{
Text = "Checked Folders will be Displayed: ",
FontSize = 16,
FontWeight = FontWeights.Bold,
};
sp.Children.Add(tb);
foreach (var fs in FolderShowList)
{
var cb = new CheckBox()
{
Content = fs.Label,
IsChecked = fs.Show,
IsEnabled = fs.Hidable,
FontSize = 14,
FontWeight = FontWeights.Bold,
Margin = new Thickness(0, 5, 0, 0)
};
cb.Click += new RoutedEventHandler(Event_CheckBoxFolderList_Click);
sp.Children.Add(cb);
}
var bp = new StackPanel()
{
Orientation = Orientation.Horizontal,
Margin = new Thickness(0, 5, 0, 0)
};
var ba = new Button() { Content = "Apply", Width = 50, Height = 25, BorderBrush = new SolidColorBrush(Colors.Transparent), Tag = pu };
ba.Click += new RoutedEventHandler(Event_ApplyFolderList_Click);
bp.Children.Add(ba);
var bc = new Button() { Content = "Cancel", Width = 50, Height = 25, BorderBrush = new SolidColorBrush(Colors.Transparent), Tag = pu, Margin = new Thickness(7, 0, 0, 0) };
bc.Click += new RoutedEventHandler(Event_CancelFolderList_Click);
bp.Children.Add(bc);
sp.Children.Add(bp);
var tbm = new TextBlock()
{
Text = "Disabled folders cannot be hidden.",
Margin = new Thickness(0, 5, 0, 0),
FontSize = 12,
Foreground = new SolidColorBrush() { Color = Colors.Red }
};
sp.Children.Add(tbm);
b.Child = sp;
pu.Child = b;
return pu;
}
private static void Event_CheckBoxFolderList_Click(object sender, RoutedEventArgs e)
{
var cb = (CheckBox)e.Source;
foreach (var fs in FolderShowList)
{
if (fs.Label == cb.Content as string)
{
fs.Show = (bool)cb.IsChecked;
break;
}
}
}
private static void Event_CancelFolderList_Click(object sender, RoutedEventArgs e)
{
((Popup)((Button)e.Source).Tag).IsOpen = false;
}
private static void Event_ApplyFolderList_Click(object sender, RoutedEventArgs e)
{
foreach (var fs in FolderShowList)
{
var sh = LabelData[fs.Label];
LabelData[fs.Label] = new Tuple<bool, bool>(fs.Show, sh.Item2);
}
((Popup)((Button)e.Source).Tag).IsOpen = false;
int p = 0;
foreach (var f in Me)
{
var fs = LabelData[f.Label].Item1 == true;
f.Top = p * folderPositionFactor;
f.FolderVisible = fs ? Visibility.Visible : Visibility.Collapsed;
if (fs) p += 1;
}
foreach (ItemsControl ic in GD.MainWindow.xFolders.Children)
{
for (int i = 0; i < ic.Items.Count; i++)
{
var ie = (UIElement)ic.ItemContainerGenerator.ContainerFromIndex(i);
var iec = Controls.FindChildByType<Canvas>(ie, "FolderCanvas");
if (iec != null)
{
var f = Me[i];
Canvas.SetTop(iec, f.Top); // Why does this not work and
iec.Visibility = f.FolderVisible; // this does work?
}
}
break;
}
GD.Refresh.RaiseEvent(new RoutedEventArgs(Button.ClickEvent));
}
public static void Placement(ref ListBox lb)
{
lb.Items.Clear();
foreach (ItemsControl ic in GD.MainWindow.xFolders.Children)
{
for (int i = 0; i < ic.Items.Count; i++)
{
var ie = (UIElement)ic.ItemContainerGenerator.ContainerFromIndex(i);
var iec = Controls.FindChildByType<Canvas>(ie, "FolderCanvas");
if (iec != null)
{
var f = Me[i];
lb.Items.Add(f.Label + ": " + f.Top.ToString() + ", " + (f.FolderVisible == Visibility.Visible ? "True" : "False"));
}
}
break;
}
}
public Folders()
{// The constructor, initializes itself by creating a folder for each label in Labels
FolderShowList = new List<FolderShow>();
for (int i = 0; i < Labels.Length; i++)
{
string n = Labels[i];
bool h = LabelData[Labels[i]].Item2;
bool s = h == true ? LabelData[Labels[1]].Item1 == true : true;
FolderShowList.Add(new FolderShow(n, s, h));
}
SetFolders();
Me = this;
}
const double folderPositionFactor = 31; // 21.80;
public void SetFolders()
{
SolidColorBrush[] scb = new SolidColorBrush[Labels.Length]; // Hold the linear solid color brush to assign to the folder.
Color[] pointerColor = new Color[Labels.Length]; // Hold the color to assign to the folder's solid color brush.
for (int i = 0; i < Labels.Length; i++)
{// Create a solid color brush for each folder.
ColorString2ARGB(ref ARGBColor, NextColor()); // Color into byte array.
Color TmpColor = Color.FromArgb(ARGBColor[0], ARGBColor[1], ARGBColor[2], ARGBColor[3]); // Create top color.
pointerColor[i] = TmpColor;
SolidColorBrush TmpSCB = new SolidColorBrush() { Color = TmpColor };
scb[i] = TmpSCB; // Assign the solid color brush.
}
// All is ready to create the individual folders.
const string folderHeight = "56"; // "44";
Brush FontColor = Brushes.Black; // Initial font color for labels.
string fp = "M0,7 A7,7 90 0 1 7,0 L100,0 105,18 150,18 150,FH 0,FH Z".Replace("FH", folderHeight); // Initial geometric path for folder design.
int afp = 0; // Actual Folder Position.
for (int i = 0; i < Labels.Length; i++)
{// Create the individual folders.
bool fs = FolderShowList[i].Show;
Add(new Folder(
i + 1, // Folder sequence count.
Labels[i], // Folder label.
afp * folderPositionFactor, // Position of top of folder.
scb[i % scb.Length], // Solid color brush.
fp, // Geometric path for folder design.
fs ? Visibility.Visible : Visibility.Collapsed // User Hidden.
)
);
if (fs) afp += 1;
if (i == 0)
{// First folder created, now set values for remaining folders.
FontColor = Brushes.White;
fp = "M0,25 A7,7 90 0 1 7,18 L13,18 18,0 100,0 105,18 150,18 150,FH 0,FH Z".Replace("FH", folderHeight);
}
}
}
}
public static class Controls
{
public static T FindChildByType<T>(DependencyObject parent, string childName) where T : DependencyObject
{
if (parent == null) return null; // No parent, get out of here.
T foundChild = null;
int childrenCount = VisualTreeHelper.GetChildrenCount(parent);
for (int i = 0; i < childrenCount; i++)
{
var child = VisualTreeHelper.GetChild(parent, i);
// If the child is not of the request child type child
T childType = child as T;
if (childType == null)
{
// recursively drill down the tree
foundChild = FindChildByType<T>(child, childName);
// If the child is found, break so we do not overwrite the found child.
if (foundChild != null) break;
}
else if (!string.IsNullOrEmpty(childName))
{
var frameworkElement = child as FrameworkElement;
// If the child's name is set for search
if (frameworkElement != null && frameworkElement.Name == childName)
{
// if the child's name is of the request name
foundChild = (T)child;
break;
}
}
else
{
// child element found.
foundChild = (T)child;
break;
}
}
return foundChild;
}
}
}
Thanks for any help that you can provide.
I finally figured out the answer, and it is very simple.
This is the code to be changed (the center line only):
var f = Me[i];
Canvas.SetTop(iec, f.Top); // Why does this not work
iec.Visibility = f.FolderVisible; // and this does work?
This is the new code (only the center line is changed, actually, only one character is removed):
var f = Me[i];
Canvas.SetTop(ie, f.Top);
iec.Visibility = f.FolderVisible;
That is, change Canvas.SetTop(iec, f.Top); to Canvas.SetTop(ie, f.Top); that is, only change iec to ie and all works fine.
Related
TabControl overlays Minimize, Maximize and Close button of window
If I create for example 12 Tabs at start of my app the tabpages are overlapping the Close, Minimize and Maximize buttons, I have used the official sample from Microsoft: https://github.com/microsoft/WinUI-Gallery/blob/master/XamlControlsGallery/TabViewPages/TabViewWindowingSamplePage.xaml.cs I only changed the number of tabs created at start to 12 like this: // Main Window -- add some default items for (int i = 0; i < 12; i++) { Tabs.TabItems.Add(CreateNewTVI($"Item {i}", $"Page {i}")); } And then this happens: And I really don't know why this happens Edit: The Xaml Code: <Grid HorizontalAlignment="Stretch" VerticalAlignment="Stretch"> <muxc:TabView x:Name="Tabs" VerticalAlignment="Stretch" <muxc:TabView.TabStripHeader> <Grid x:Name="ShellTitlebarInset" Background="Transparent" /> </muxc:TabView.TabStripHeader> <muxc:TabView.TabStripFooter> <Grid x:Name="CustomDragRegion" Background="Transparent" HorizontalAlignment="Right" Width="188"/> </muxc:TabView.TabStripFooter> </muxc:TabView> </Grid> The MainPage.xaml.cs public MainPage() { this.InitializeComponent(); } protected override void OnNavigatedTo(NavigationEventArgs e) { base.OnNavigatedTo(e); Tabs.SelectedIndex = 0; // Extend into the titlebar var coreTitleBar = CoreApplication.GetCurrentView().TitleBar; coreTitleBar.ExtendViewIntoTitleBar = true; coreTitleBar.LayoutMetricsChanged += CoreTitleBar_LayoutMetricsChanged; var titleBar = ApplicationView.GetForCurrentView().TitleBar; titleBar.ButtonBackgroundColor = Windows.UI.Colors.Transparent; titleBar.ButtonInactiveBackgroundColor = Windows.UI.Colors.Transparent; Window.Current.SetTitleBar(CustomDragRegion); // Main Window - add some tabs for (int i = 0; i < 24; i++) { Tabs.TabItems.Add(CreateNewTVI($"Item {i}", $"Page {i}")); } } private void CoreTitleBar_LayoutMetricsChanged(CoreApplicationViewTitleBar sender, object args) { // To ensure that the tabs in the titlebar are not occluded by shell // content, we must ensure that we account for left and right overlays. // In LTR layouts, the right inset includes the caption buttons and the // drag region, which is flipped in RTL. // The SystemOverlayLeftInset and SystemOverlayRightInset values are // in terms of physical left and right. Therefore, we need to flip // then when our flow direction is RTL. if (FlowDirection == FlowDirection.LeftToRight) { CustomDragRegion.MinWidth = sender.SystemOverlayRightInset; ShellTitlebarInset.MinWidth = sender.SystemOverlayLeftInset; } else { CustomDragRegion.MinWidth = sender.SystemOverlayLeftInset; ShellTitlebarInset.MinWidth = sender.SystemOverlayRightInset; } // Ensure that the height of the custom regions are the same as the titlebar. CustomDragRegion.Height = ShellTitlebarInset.Height = sender.Height; } private TabViewItem CreateNewTVI(string header, string dataContext) { var newTab = new TabViewItem() { IconSource = new Microsoft.UI.Xaml.Controls.SymbolIconSource() { Symbol = Symbol.Placeholder }, Header = header, Content = new TextBlock() { Text = "This is a text:\n" + dataContext, HorizontalAlignment = HorizontalAlignment.Center, VerticalAlignment = VerticalAlignment.Center, FontSize = 25, } }; return newTab; }
To fix this, you have to first add a Loaded event to your CustomDragRegion. Now you can remove the code from the OnNavigateTo function and paste it into the Loaded function. That's it. private void CustomDragRegion_Loaded(object sender, RoutedEventArgs e) { Tabs.SelectedIndex = 0; // Extend into the titlebar var coreTitleBar = CoreApplication.GetCurrentView().TitleBar; coreTitleBar.ExtendViewIntoTitleBar = true; coreTitleBar.LayoutMetricsChanged += CoreTitleBar_LayoutMetricsChanged; var titleBar = ApplicationView.GetForCurrentView().TitleBar; titleBar.ButtonBackgroundColor = Windows.UI.Colors.Transparent; titleBar.ButtonInactiveBackgroundColor = Windows.UI.Colors.Transparent; Window.Current.SetTitleBar(CustomDragRegion); // Main Window - add some tabs for (int i = 0; i < 24; i++) { Tabs.TabItems.Add(CreateNewTVI($"Item {i}", $"Page {i}")); } }
Need help to improve the appearance on Xamarin iOS tab
I want to improve the size and background of my content page on ios, var About = new ContentPage() { Title = "About" }; var layout = new StackLayout(); var line1 = new Label() { Text = viewModel.Member.Line1, FontSize = 16, HorizontalTextAlignment = TextAlignment.Center }; var MapTab = new ContentPage() {Title = "Map"}; Android: The title of the content page appears very small on ios and not visible. I need help in trying to improve the looks and make it bigger.
You'll have to implement Custom Renderer for your TabbedPage. See this link: Extending TabbedPage in Xamarin Forms. It says, that: To do these customizations we will use a custom renderer on each platform to render the TabbedPage. I will duplicate the code snippets from the source as a example: YourTabbedPage.xaml: <TabbedPage xmlns="http://xamarin.com/schemas/2014/forms" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" x:Class="CustomTabbedPage.MainPage" xmlns:android="clr-namespace:Xamarin.Forms.PlatformConfiguration.AndroidSpecific;assembly=Xamarin.Forms.Core" android:TabbedPage.ToolbarPlacement="Bottom"> <TabbedPage.Children> <ContentPage Title="Home" Icon="ic_home.png" BackgroundColor="White"/> <ContentPage Title="Favorites" Icon="ic_favorite.png" BackgroundColor="White"/> <ContentPage Title="App" Icon="app_logo_unselected.png" x:Name="home" BackgroundColor="White"/> <ContentPage Title="Friends" Icon="ic_people.png" BackgroundColor="White"/> <ContentPage Title="Settings" Icon="ic_settings.png" BackgroundColor="White"/> </TabbedPage.Children> </TabbedPage> iOS Custom Renderer: public class ExtendedTabbedPageRenderer : TabbedRenderer { public override void ViewWillAppear(bool animated) { if (TabBar?.Items == null) return; var tabs = Element as TabbedPage; if (tabs != null) { for (int i = 0; i < TabBar.Items.Length; i++) { UpdateTabBarItem(TabBar.Items[i], tabs.Children[i].Icon); } } base.ViewWillAppear(animated); } private void UpdateTabBarItem(UITabBarItem item, string icon) { if (item == null || icon == null) return; // Set the font for the title. item.SetTitleTextAttributes(new UITextAttributes() { Font = UIFont.FromName("GillSans-UltraBold", 12), TextColor = Color.FromHex("#757575").ToUIColor() }, UIControlState.Normal); item.SetTitleTextAttributes(new UITextAttributes() { Font = UIFont.FromName("GillSans-UltraBold", 12), TextColor = Color.FromHex("#3C9BDF").ToUIColor() }, UIControlState.Selected); } } Android Custom Renderer: public class ExtendedTabbedPageRenderer : TabbedPageRenderer { Xamarin.Forms.TabbedPage tabbedPage; BottomNavigationView bottomNavigationView; Android.Views.IMenuItem lastItemSelected; int lastItemId=-1; protected override void OnElementChanged(ElementChangedEventArgs<Xamarin.Forms.TabbedPage> e) { base.OnElementChanged(e); if (e.NewElement != null) { tabbedPage = e.NewElement as ExtendedTabbedPage; bottomNavigationView = (GetChildAt(0) as Android.Widget.RelativeLayout).GetChildAt(1) as BottomNavigationView; bottomNavigationView.NavigationItemSelected += BottomNavigationView_NavigationItemSelected; //Call to change the font ChangeFont(); } if (e.OldElement != null) { bottomNavigationView.NavigationItemSelected -= BottomNavigationView_NavigationItemSelected; } } //Change Tab font void ChangeFont() { var fontFace = Typeface.CreateFromAsset(Context.Assets, "gilsansultrabold.ttf"); var bottomNavMenuView = bottomNavigationView.GetChildAt(0) as BottomNavigationMenuView; for (int i = 0; i < bottomNavMenuView.ChildCount; i++) { var item = bottomNavMenuView.GetChildAt(i) as BottomNavigationItemView; var itemTitle = item.GetChildAt(1); var smallTextView = ((TextView)((BaselineLayout)itemTitle).GetChildAt(0)); var largeTextView = ((TextView)((BaselineLayout)itemTitle).GetChildAt(1)); lastItemId = bottomNavMenuView.SelectedItemId; smallTextView.SetTypeface(fontFace, TypefaceStyle.Bold); largeTextView.SetTypeface(fontFace, TypefaceStyle.Bold); //Set text color var textColor = (item.Id == bottomNavMenuView.SelectedItemId) ? tabbedPage.On<Xamarin.Forms.PlatformConfiguration.Android>().GetBarSelectedItemColor().ToAndroid() : tabbedPage.On<Xamarin.Forms.PlatformConfiguration.Android>().GetBarItemColor().ToAndroid(); smallTextView.SetTextColor(textColor); largeTextView.SetTextColor(textColor); } } void BottomNavigationView_NavigationItemSelected(object sender, BottomNavigationView.NavigationItemSelectedEventArgs e) { var normalColor = tabbedPage.On<Xamarin.Forms.PlatformConfiguration.Android>().GetBarItemColor().ToAndroid(); var selectedColor = tabbedPage.On<Xamarin.Forms.PlatformConfiguration.Android>().GetBarSelectedItemColor().ToAndroid(); if(lastItemId!=-1) { SetTabItemTextColor(bottomNavMenuView.GetChildAt(lastItemId) as BottomNavigationItemView, normalColor); } SetTabItemTextColor(bottomNavMenuView.GetChildAt(e.Item.ItemId) as BottomNavigationItemView, selectedColor); this.OnNavigationItemSelected(e.Item); lastItemId = e.Item.ItemId; } void SetTabItemTextColor(BottomNavigationItemView bottomNavigationItemView, Android.Graphics.Color textColor) { var itemTitle = bottomNavigationItemView.GetChildAt(1); var smallTextView = ((TextView)((BaselineLayout)itemTitle).GetChildAt(0)); var largeTextView = ((TextView)((BaselineLayout)itemTitle).GetChildAt(1)); smallTextView.SetTextColor(textColor); largeTextView.SetTextColor(textColor); } }
I created a content class for the two pages I wanted to improve on, the map and the memberaboutpage and I instead of using the content page, I did this var About = new MemberAboutPage { Title = "About" }; var layout = new StackLayout(); var MapTab = new MapPage() { Title = "Map" }; Then I added the pages to the pages I created and mirrored to the ios rendere page below, this page formats the tabs and makes them more nicer looking and alos prevents the overlapping on iPhone X. Happy Programming Mates '[assembly: ExportRenderer(typeof(CardPage), typeof(MyiOSTabbedPage))] [assembly: ExportRenderer(typeof(LoginPage), typeof(MyiOSTabbedPage))] [assembly: ExportRenderer(typeof(MemberAboutPage), typeof(MyiOSTabbedPage))] [assembly: ExportRenderer(typeof(MapPage), typeof(MyiOSTabbedPage))] namespace CHA.iOS.Renderers { public class MyiOSTabbedPage : PageRenderer { public override void ViewWillLayoutSubviews() { base.ViewWillLayoutSubviews(); nfloat tabSize = 44.0f; UIInterfaceOrientation orientation = UIApplication.SharedApplication.StatusBarOrientation; CGRect rect = this.View.Frame; rect.Y = this.NavigationController != null ? tabSize : tabSize + 20; this.View.Frame = rect; if (TabBarController != null) { CGRect tabFrame = this.TabBarController.TabBar.Frame; tabFrame.Height = tabSize; tabFrame.Y = this.NavigationController != null ? 0 : 0; this.TabBarController.TabBar.Frame = tabFrame; this.TabBarController.TabBar.BarTintColor = UIColor.FromRGB(234,232,232); var textAttr = new UITextAttributes { Font = UIFont.SystemFontOfSize(20) }; var selectedAttr = new UITextAttributes { TextColor = UIColor.FromRGB(63,165,186), Font=UIFont.BoldSystemFontOfSize(20) }; foreach (var i in this.TabBarController.TabBar.Items) { i.SetTitleTextAttributes(textAttr, UIControlState.Normal); i.SetTitleTextAttributes(selectedAttr, UIControlState.Selected); } } } }'
Set Rectangle width by Items in list C# WPF
How do I set the width of a rectangle, by dividing the number of Items in a list via a user control in C#, using WPF has to be in code behind and not using Xaml: public partial class UserControl1 : UserControl { private List<Color> colours; private int Highest; private int Lowest; private int Median; public int Width {get; set;} private Data mydata; public Data data { get { return mydata; } set { mydata = value; BindData(); } } public UserControl1() { colours = new List<Color>() { Colors.Red, Colors.White, Colors.Blue}; InitializeComponent(); if (Width == 0) { Width = 300; } } private void BindData() { //work out rectangle width Highest = Convert.ToInt32(mydata.Values.Max()); Lowest = Convert.ToInt32(mydata.Values.Min()); Median = Convert.ToInt32(mydata.Values.Average()); foreach (double item in mydata.Values) { //create rectangle of width specified SetWidth(); } } private void SetWidth() { //divide number of items in list by total to define Width } private Color GetColourFromValue(int totalNumber) { var numOfColours = colours.Count; if (totalNumber >= numOfColours) { return colours[totalNumber % numOfColours]; } return colours[totalNumber]; } private Color GetGradeColour(byte itemNumber, byte totalNumberItems, Color ItemColour) { byte multipler = (byte)(200 / totalNumberItems); byte a = (byte)((itemNumber + 1) * multipler); return Color.FromArgb(a, ItemColour.R, ItemColour.G, ItemColour.B); } private Rectangle CreateRectangle(double Value, int Width, Color ItemColor)//double value, width) { //create rectangle var rectangle = new Rectangle() { //Fill = new SolidColorBrush(ItemColor) }; //give rectangle width return rectangle; } }
If you want a set of controls to automatically share the horizontal space of a parent UserControl then use a Grid control with columns. This is much easier than trying the calculate the widths manually. <Grid> <Grid.ColumnDefinitions> <ColumnDefinition/> <ColumnDefinition/> <ColumnDefinition/> <ColumnDefinition/> </Grid.ColumnDefinitions> <Rectangle Grid.Column="0"/> <Rectangle Grid.Column="1"/> <Rectangle Grid.Column="2"/> <Rectangle Grid.Column="3"/> </Grid> Code: var grid = new Grid { ColumnDefinitions = { new ColumnDefinition(), new ColumnDefinition(), new ColumnDefinition(), new ColumnDefinition() } }; var rectangle1 = new Rectangle { Fill = Brushes.Blue }; rectangle1.SetValue(Grid.ColumnProperty, 0); grid.Children.Add(rectangle1); var rectangle2 = new Rectangle { Fill = Brushes.Red }; rectangle2.SetValue(Grid.ColumnProperty, 1); grid.Children.Add(rectangle2); var rectangle3 = new Rectangle { Fill = Brushes.Green }; rectangle3.SetValue(Grid.ColumnProperty, 2); grid.Children.Add(rectangle3); var rectangle4 = new Rectangle { Fill = Brushes.Yellow }; rectangle4.SetValue(Grid.ColumnProperty, 3); grid.Children.Add(rectangle4);
How to Rearrange rectangles on canvas after modifying height of any rectangle in wpf?
I am adding Rectangle from grid cell values that is being entered by user directly in grid rows. When i modify value of specific column say Thickness i.e Height then then it increases Height of selected row rectangle but it doesnt rearrange all rectangle below it exactly after the selected row rectangle. In xaml.cs public class MyLayer : INotifyPropertyChanged { public string Thickness { get; set; } public string OffsetRight { get; set; } public string OffsetLeft { get; set; } public string Material { get; set; } public string MaterialPopup { get; set; } public Rectangle rectangle { get; set; } public GlassRectangle GlassRectangle { get; set; } public MaterialLayer() { GlassRectangle = new GlassRectangle(); } event PropertyChangedEventHandler INotifyPropertyChanged.PropertyChanged { add { } remove { } } } public class GlassRectangle { public Rectangle Rectangle { get; set; } public double Top = 0; public GlassRectangle() { Rectangle = new Rectangle(); } } private void gridInner_CellValueChanged(object sender, DevExpress.Xpf.Grid.CellValueChangedEventArgs e) { string cellValue = string.Empty; MyLayer currentLayer = ((MyLayer)(e.Row)); if (e.Column.HeaderCaption.ToString() == "Thickness") { cellValue =(e.Value.ToString()); //there is alredy a rectangle - means this is edit mode if (currentLayer.rectangle != null) { currentLayer.rectangle.Height = Convert.ToDouble(cellValue); currentLayer.rectangle.Stroke = new SolidColorBrush(Color.FromRgb(0, 255, 0)); } //else this is insert mode else { currentLayer.rectangle = CreateRectangle(cellValue); } } } protected Rectangle CreateRectangle(string cellval) { Rectangle newrect = new Rectangle(); newrect.Stroke = Brushes.Red; newrect.StrokeThickness = 1; if (cellval.ToString().Contains(".")) { newrect.Height = Convert.ToDouble(cellval) * 100; } else { newrect.Height = Convert.ToDouble(cellval); } newrect.Width = width; Canvas.SetLeft(newrect, 100); double canvasTop = 0.0; if (canvasboard.Children.Count > 0) { var lastChildIndex = canvasboard.Children.Count - 1; var lastChild = canvasboard.Children[lastChildIndex] as FrameworkElement; if (lastChild != null) //lastChild.Height-1: so that it come extactly on existing if set to +1 it comes below first rectangle canvasTop = Canvas.GetTop(lastChild) + lastChild.Height - 1; } Canvas.SetTop(newrect, canvasTop); val = val + 1; newrect.Tag = val; canvasboard.Children.Add(newrect); //rectangle = rect; foreach (UIElement ui in canvasboard.Children) { if (ui.GetType() == typeof(Rectangle)) { itemstoremove.Add(ui); } } return newrect; } NEW EVENT Method: private void gridMaterialInner_CellValueChanged(object sender, DevExpress.Xpf.Grid.CellValueChangedEventArgs e) { string cellValue = string.Empty; string cellOldValue = string.Empty; MyLayer currentLayer = ((MyLayer)(e.Row)); if (e.Column.HeaderCaption.ToString() == "Thickness") { //current cell value cellValue =(e.Value.ToString());// GetRowCellValue(e.RowHandle, gridMaterialInner.Columns["LastName"]).ToString(); //there is alredy a rectangle - means this is edit mode double currentheight = 0.0; double oldht = 0.0; // old cell value if (e.OldValue != null) { cellOldValue = (e.OldValue.ToString()); } if (currentLayer.rectangle != null) { if (cellValue.ToString().Contains(".")) { currentheight = Convert.ToDouble(cellValue) * 100; } else { currentheight = Convert.ToDouble(cellValue) * 100; } if (cellOldValue.ToString().Contains(".")) { oldht = Convert.ToDouble(cellOldValue) * 100; } else if(cellOldValue!=string.Empty) { oldht = Convert.ToDouble(cellOldValue) * 100; } currentLayer.rectangle.Height = currentheight; currentLayer.rectangle.Stroke = new SolidColorBrush(Color.FromRgb(0, 255, 0)); //Refresh(); //Get the index of selected row int layerIndex = materialBindlist.IndexOf(currentLayer); for(int i = layerIndex; i < materialBindlist.Count-1; i++) { //set the top position of all other rectangles that are below selected rectangle/row //(Current-Old)+Top Canvas.SetTop(materialBindlist[i + 1].rectangle, (currentheight - oldht) + materialBindlist[i + 1].GlassRectangle.Top); //Canvas.SetTop(materialBindlist[i].rectangle, (currentheight - oldht) + materialBindlist[i + 1].GlassRectangle.Top); } } //else this is insert mode else { //MaterialLayer object currentLayer.rectangle = CreateRectangle(cellValue); //store Top & Rectangle object in GlassRectangle class which is referenced in MaterialLayer class currentLayer.GlassRectangle.Rectangle = currentLayer.rectangle; currentLayer.GlassRectangle.Top = canvasTop; } } } This create rectangle one after other like Stacked item on canvas. But when i modify the value of Thickness column which is Height of Rectangle it reflects on canvas but the Other Rectangle below must appear after the Changed Height of current rectangle. Note: I cant use WrapPanel in my application. Just to modify existing code using Canvas. Help Appreciated! Modified For Loop in CellChange Event: int layerIndex = materialBindlist.IndexOf(currentLayer); for(int i = layerIndex; i < materialBindlist.Count-1; i++) { //set the top position of all other rectangles that are below selected rectangle/row //(Current-Old)+Top double top=Convert.ToDouble((currentHeight - oldHeight) + materialBindlist[i + 1].GlassRectangle.Top); Canvas.SetTop(materialBindlist[i + 1].rectangle,top); materialBindlist[i + 1].GlassRectangle.Top = top; }
What you're looking for can be done even with a Canvas however you should really consider using something like an ItemsControl for this. Solution when forced to use Canvas: private void Refresh() { for (int i = 1; i < canvasboard.Children.Count; ++i) { var currentElement = canvasboard.Children[i] as FrameworkElement; var previousElement = canvasboard.Children[i - 1] as FrameworkElement; if (currentElement == null || previousElement == null) return; var requiredTop = Canvas.GetTop(previousElement) + previousElement.Height - 1; if (Math.Abs(Canvas.GetTop(currentElement) - requiredTop) > 0.0) Canvas.SetTop(currentElement, requiredTop); } } Now call this function "after" you change the size of an existing element in the Canvas and it will re-position the elements accordingly to suit the new dimension. In your code, it would be called from the gridInner_CellValueChanged(...) function after you set the new height in "edit" mode. What you should try to do: If your able to persuade whoever you need to and get to use something like an ItemsControl, this will be so much simpler. say a rough example: xaml could be: <ItemsControl ItemsSource="{Binding Items}"> <ItemsControl.ItemsPanel> <ItemsPanelTemplate> <VirtualizingStackPanel Orientation="Vertical" /> </ItemsPanelTemplate> </ItemsControl.ItemsPanel> </ItemsControl> with Items declared as public ObservableCollection<Rectangle> Items { get; set; } in code. Now your Add() function could just be: private void Add() { var rect = new Rectangle { Stroke = Brushes.Red, StrokeThickness = 1, Height = Convert.ToDouble(txtheight.Text), Width = 100 }; Items.Add(rect); } and as for updates when your editing existing control's that would be automatic in this case. There is no hard-coded positioning as the Layout container takes care of all that mess for you. You can ofcourse switch the Items collection type to your own custom control type MyLayer and with it implementing INPC, changes would still continue to be automatic. You'd have to define a DataTemplate now to get your Item to be rendered but that's like 3 lines of work in just xaml. You can also just work of the Items property directly when needing to tweak an exisiting control than having to reference the ItemsControl in code-behind. Binding's should take care of the updates to the view automatically.
Modified for loop in Cell Change Event: int layerIndex = materialBindlist.IndexOf(currentLayer); for(int i = layerIndex; i < materialBindlist.Count-1; i++) { //set the top position of all other rectangles that are below selected rectangle/row //(Current-Old)+Top double top=Convert.ToDouble((currentHeight - oldHeight) + materialBindlist[i + 1].GlassRectangle.Top); Canvas.SetTop(materialBindlist[i + 1].rectangle,top); materialBindlist[i + 1].GlassRectangle.Top = top; } it works now..!Thanks to all!
Picture Puzzle: Set one Image to the same size and position to another image
today I already asked you a question for my picture puzzle (Original Question). I started to rewrite my code for better performance. And I got the most important part done! But I have another problem.. I generate a gray overlay image to hide the image, but because I want to handle dynamic size of pictures I cannot set fixed width and height values for the "mask". So probably not the whole picture is overlayed with my "mask". Does somebody have a solution for this? I don't know how to set the position and size of my mask exactly to the position and size of the picture. I attached a screencast for demonstration. XAML: <Window x:Class="PicturePuzzle.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="MainWindow" Loaded="WindowLoaded"> <Grid> <Grid.RowDefinitions> <RowDefinition Height="*" /> <RowDefinition Height="80" /> </Grid.RowDefinitions> <Grid x:Name="grid" Margin="5" HorizontalAlignment="Center" VerticalAlignment="Top"> <Image x:Name="imgPicture" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Source="Images/puzzle.gif" Stretch="Uniform" /> <Image x:Name="imgMask" RenderOptions.EdgeMode="Aliased" /> </Grid> <StackPanel Grid.Row="1" HorizontalAlignment="Center" VerticalAlignment="Center"> <StackPanel Margin="0,0,0,10" Orientation="Horizontal"> <Button x:Name="btnStart" Width="60" Margin="0,0,5,0" Click="BtnStartClick" Content="Start" /> <Button x:Name="btnStop" Width="60" Click="BtnStopClick" Content="Stop" IsEnabled="False" /> <ToggleButton x:Name="btnSolution" Margin="5,0,0,0" Checked="btnSolution_Checked" Content="Lösung anzeigen" Unchecked="btnSolution_Unchecked" /> </StackPanel> <Slider x:Name="slSpeed" IsDirectionReversed="True" Maximum="10" Minimum="1" ValueChanged="SlSpeedValueChanged" Value="10" /> </StackPanel> </Grid> </Window> Codebehind: using System; using System.Collections.Generic; using System.IO; using System.Windows; using System.Windows.Media; using System.Windows.Media.Imaging; using System.Windows.Threading; namespace PicturePuzzle { /// <summary> /// Interaction logic for MainWindow.xaml /// </summary> public partial class MainWindow { public MainWindow() { InitializeComponent(); _positionAlphaValues = new Dictionary<Point, byte>(); _images = new List<FileInfo>(); using (var s = new StreamReader("Images.txt")) { while (!s.EndOfStream) { var line = s.ReadLine(); if (string.IsNullOrWhiteSpace(line)) { continue; } var fi = new FileInfo(line); if (!fi.Exists) { continue; } _images.Add(fi); } } } private const int MaxQuadsX = 5; private const int MaxQuadsY = 5; private readonly List<FileInfo> _images; private DispatcherTimer _timer; private DispatcherTimer _alphaTimer; private WriteableBitmap _bitmap; private Size _size; private List<Point> _positions; private Dictionary<Point, byte> _positionAlphaValues; private int _tickCounter; private int _imageCounter; private int _quadWidth; private int _quadHeight; private void WindowLoaded(object sender, RoutedEventArgs e) { _size = imgPicture.RenderSize; var width = (int)Math.Ceiling(_size.Width); var height = (int)Math.Ceiling(_size.Height); _quadWidth = width / MaxQuadsX; _quadHeight = height / MaxQuadsY; imgPicture.Width = _quadWidth * MaxQuadsX - 5; imgPicture.Height = _quadHeight * MaxQuadsY - 5; _bitmap = new WriteableBitmap(width, height, 96, 96, PixelFormats.Bgra32, null); imgMask.Source = _bitmap; } #region Click handlers private void BtnStartClick(object sender, RoutedEventArgs e) { btnStart.IsEnabled = false; btnStop.IsEnabled = true; btnSolution.IsChecked = false; // set the real picture _imageCounter = 0; _images.Shuffle(); SetPuzzlePicture(); _timer = new DispatcherTimer { Interval = TimeSpan.FromSeconds(slSpeed.Value / 10) }; _timer.Tick += TimerTick; _timer.Start(); _alphaTimer = new DispatcherTimer { Interval = TimeSpan.FromSeconds(0.1) }; _alphaTimer.Tick += AlphaTimerOnTick; _alphaTimer.Start(); } private void BtnStopClick(object sender, RoutedEventArgs e) { btnStart.IsEnabled = true; btnStop.IsEnabled = false; _timer.Stop(); _alphaTimer.Stop(); } private void SlSpeedValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e) { if (_timer != null) { _timer.Interval = TimeSpan.FromSeconds(slSpeed.Value / 10); } } private void btnSolution_Checked(object sender, RoutedEventArgs e) { btnStop.IsEnabled = false; StopTimers(); imgMask.Visibility = Visibility.Hidden; } private void btnSolution_Unchecked(object sender, RoutedEventArgs e) { btnStart.IsEnabled = true; btnStop.IsEnabled = false; ResetMaskImage(); } #endregion private void SetPuzzlePicture() { _positionAlphaValues.Clear(); ResetMaskImage(); var imgFile = _images[_imageCounter++]; var image = new BitmapImage(); image.BeginInit(); image.UriSource = new Uri(imgFile.FullName, UriKind.Absolute); image.EndInit(); imgPicture.Source = image; } private void TimerTick(object sender, EventArgs e) { if (_tickCounter >= _positions.Count) { if (_imageCounter >= _images.Count) { _timer.Stop(); btnStart.IsEnabled = true; btnStop.IsEnabled = false; return; } SetPuzzlePicture(); } var randomPoint = _positions[_tickCounter++]; _positionAlphaValues.Add(randomPoint, 255); } private void AlphaTimerOnTick(object sender, EventArgs eventArgs) { var updatedList = new Dictionary<Point, byte>(); foreach (var e in _positionAlphaValues) { var newValue = e.Value - (11 - slSpeed.Value) * 5; if (newValue <= 0) { continue; } SetAlphaChannel(e.Key, (byte)newValue); updatedList.Add(e.Key, (byte)newValue); } _positionAlphaValues = updatedList; } private void StopTimers() { if (_timer != null) { _timer.Stop(); } if (_alphaTimer != null) { _alphaTimer.Stop(); } } private void ResetMaskImage() { imgMask.Visibility = Visibility.Visible; var width = _quadWidth * MaxQuadsX; var height = _quadHeight * MaxQuadsY; var size = width * height * 4; var buffer = new byte[size]; for (int i = 0; i < size; i++) { buffer[i++] = 128; buffer[i++] = 128; buffer[i++] = 128; buffer[i] = 255; } var area = new Int32Rect(0, 0, width, height); _bitmap.WritePixels(area, buffer, width * 4, 0); _positions = GetPositions(); _tickCounter = 0; } private void SetAlphaChannel(Point point, byte alpha) { var size = _quadWidth * _quadHeight * 4; var buffer = new byte[size]; for (int i = 0; i < size; i++) { buffer[i++] = 128; buffer[i++] = 128; buffer[i++] = 128; buffer[i] = alpha; } var startX = (int)point.X * _quadWidth; var startY = (int)point.Y * _quadHeight; var area = new Int32Rect(startX, startY, _quadWidth, _quadHeight); _bitmap.WritePixels(area, buffer, _quadWidth * 4, 0); } private List<Point> GetPositions() { var generated = new List<Point>(); for (int y = 0; y < MaxQuadsY; y++) { for (int x = 0; x < MaxQuadsX; x++) { var point = new Point(x, y); generated.Add(point); } } generated.Shuffle(); return generated; } } } Extensions.cs using System; using System.Collections.Generic; using System.Windows.Media; using System.Windows.Media.Imaging; namespace PicturePuzzle { public static class Extensions { public static Color GetPixel(this WriteableBitmap wbm, int x, int y) { if (y > wbm.PixelHeight - 1 || x > wbm.PixelWidth - 1) return Color.FromArgb(0, 0, 0, 0); if (y < 0 || x < 0) return Color.FromArgb(0, 0, 0, 0); if (!wbm.Format.Equals(PixelFormats.Bgra32)) return Color.FromArgb(0, 0, 0, 0); IntPtr buff = wbm.BackBuffer; int stride = wbm.BackBufferStride; Color c; unsafe { var pbuff = (byte*)buff.ToPointer(); int loc = y * stride + x * 4; c = Color.FromArgb( pbuff[loc + 3], pbuff[loc + 2], pbuff[loc + 1], pbuff[loc]); } return c; } public static void Shuffle<T>(this IList<T> list) { var rng = new Random(); int n = list.Count; while (n > 1) { n--; int k = rng.Next(n + 1); T value = list[k]; list[k] = list[n]; list[n] = value; } } } } Example Images.txt (has to be in your output folder, the images also): Desert.jpg Hydrangeas.jpg Jellyfish.jpg Koala.jpg Lighthouse.jpg Penguins.jpg Tulips.jpg androids.gif Chrysanthemum.jpg And the screencast: Screencast Link Thanks for any help!
You can set the HorizontalAlignment and VerticalAlignment of your mask Image to Stretch and the Stretch to Fill like so: <Image HorizontalAlignment="Stretch" VerticalAlignment="Stretch" x:Name="imgMask" Stretch="Fill" RenderOptions.EdgeMode="Aliased" /> This will cause the mask image to fill the grid, which will be sized to the other image.