Can't create content with DataTemplate in code behind with control defined in xaml - xamarin.forms

I have the following label in a ResourceDictionary in xaml of my ContentPage:
<ContentPage.Resources>
<ResourceDictionary>
<Label Text="I am label" x:Name="label" x:Key="label"/>
</ResourceDictionary>
</ContentPaget.Resources>
And in in my code behind I have this clicked event handler:
void Handle_Clicked(object sender, System.EventArgs e)
{
DataTemplate dataTemplate = new DataTemplate(() => label);
for (int i = 0; i < 3; i ++)
{
Label content = (Label) dataTemplate.CreateContent();
stack.Children.Add(content);
}
}
In my StackLayout called stack - only 1 label is added when the button assigned with Handle_Clicked is pressed. Why is only 1 label added - when there should be 3 labels added?

I suspect that all controls need a unique id. Since this was not working either:
void Handle_Clicked(object sender, System.EventArgs e)
{
for (int i = 0; i < 3; i ++)
{
stack.Children.Add(label);
}
}
which had brought me to try and use DataTemplate in the first place. Meaning the same object can only be added once to the view.
It can also be noted that createContent() works - but only if it is being defined in xaml (not being instantiated in code behind):
<ContentPage.Resources>
<ResourceDictionary>
<DataTemplate x:Name="dataTemplate" x:Key="dataTemplate">
<Label Text="I am label"/>
</DataTemplate>
</ResourceDictionary>
</ContentPaget.Resources>
The workaround I found was to get rid of the DataTemplate and clone the object instead before adding it:
void Handle_Clicked(object sender, System.EventArgs e)
{
for (int i = 0; i < 3; i ++)
{
var l = FastDeepCloner.DeepCloner.Clone(label);
stack.Children.Add(l);
}
}

Related

Move control freely inside AbsoluteLayout in .Net Maui

I'm trying to make a layout that allow me to move controls inside it freely, I found a working solution but it has a very strange behavior, when I try to move the label, the movement is very laggy and sometimis it has an effect like it duplicate the label.
I implemented the movement with a PanGestureRecognizer adding labels inside an AbsoluteLayout programatically with a button event
This is the XAML, with the empty AbsoluteLayout and the button at the end to add de label
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="Views.MoveControlsView"
Title="MoveControlsView">
<StackLayout>
<AbsoluteLayout
x:Name="ParentLayout"
VerticalOptions="FillAndExpand"
HorizontalOptions="FillAndExpand">
</AbsoluteLayout>
<StackLayout
HorizontalOptions="FillAndExpand"
VerticalOptions="End">
<Button
x:Name="AddLabel"
Text="Add label"
Clicked="AddLabel_Clicked"/>
</StackLayout>
</StackLayout>
</ContentPage>
This is the code behind, I generate a Label when the button is clicked and add to it the PanGestureRecognizer that I also suscribed it to the PanUpdated event.
public partial class MoveControlsView : ContentPage
{
public MoveControlsView()
{
InitializeComponent();
}
private void AddLabel_Clicked(object sender, EventArgs e)
{
var label = new Label()
{
Text = "This is a label",
BackgroundColor = Colors.LightGray,
Padding = 10
};
var panGesture = new PanGestureRecognizer();
panGesture.PanUpdated += PanGestureRecognizer_PanUpdated;
label.GestureRecognizers.Add(panGesture);
ParentLayout.Children.Add(label);
}
private void PanGestureRecognizer_PanUpdated(object sender, PanUpdatedEventArgs e)
{
var label = sender as Label;
switch (e.StatusType)
{
case GestureStatus.Running:
label.TranslationX = e.TotalX;
label.TranslationY = e.TotalY;
break;
case GestureStatus.Completed:
label.TranslateTo(label.TranslationX, label.TranslationY);
break;
}
}
}
You could make changes to the code in PanGestureRecognizer_PanUpdated event handler. Try the following code:
double tempx = 0;
double tempy = 0;
private void PanGestureRecognizer_PanUpdated(object sender, PanUpdatedEventArgs e)
{
var label = sender as Label;
switch (e.StatusType)
{
case GestureStatus.Started:
if(Device.RuntimePlatform == Device.iOS)
{
tempx = label.TranslationX;
tempy = label.TranslationY;
}
break;
case GestureStatus.Running:
if (Device.RuntimePlatform == Device.iOS)
{
label.TranslationX = e.TotalX + tempx;
label.TranslationY = e.TotalY + tempy;
}
else if (Device.RuntimePlatform == Device.Android)
{
label.TranslationX += e.TotalX;
label.TranslationY += e.TotalY;
}
break;
case GestureStatus.Completed:
tempx = label.TranslationX;
tempy = label.TranslationY;
break;
}
}
For more information, you could refer to Xamarin.Forms AbsoluteLayout and Add a pan gesture recognizer
Hope it works for you.

How to disable CTRL keyboard in WPF DataGrid

I have a datagrid where in i want to prevent user from CTRL Key press.
<ImageBrush ImageSource="/AB17035_ZLT_Client;component/Images/bum_big.PNG"
Stretch="Uniform" Opacity="0.05" />
</Border.Background>
<DataGrid Name="PlanGrid" HorizontalAlignment="Stretch" FontSize="16" RowHeight="30" HorizontalScrollBarVisibility="Visible"
VerticalAlignment="Stretch"
AutoGenerateColumns="True"
SelectionMode="Extended"
VerticalGridLinesBrush="Transparent"
Background="Transparent" RowBackground="Transparent"
ItemsSource="{Binding PlanDataView, Mode=TwoWay}"
IsReadOnly="True" SelectionUnit="FullRow" IsSynchronizedWithCurrentItem="True" Cursor="Arrow" AllowDrop="True">
<DataGrid.InputBindings>
<KeyBinding Gesture="Ctrl" Command="ApplicationCommands.NotACommand"></KeyBinding>
</DataGrid.InputBindings>
<i:Interaction.Triggers>
<i:EventTrigger EventName="Loaded">
<ei:CallMethodAction MethodName="PlanGrid_Loaded" TargetObject="{Binding}"></ei:CallMethodAction>
</i:EventTrigger>
</i:Interaction.Triggers>
</DataGrid>
If user tries for CTLR key press it should not work on wpf datagrid.
I googled and found the answer for it. Below is the piece of code to prevent from ctrl key press.
private void PreviewKeyDown(object sender, KeyEventArgs e)
{
if ((Keyboard.Modifiers & ModifierKeys.Control) == ModifierKeys.Control)
{
e.Handled = true;
MessageBox.Show("CTRL Key is not allowed");
}
}
also add this function "PreviewKeyDown" to load event of data grid as below:
public void PlanGrid_Loaded(object sender, RoutedEventArgs e)
{
planDatagrid.KeyDown += new KeyEventHandler(PreviewKeyDown);
}
Code Behind
InitializeComponent();
dvSalesEntryDataGrid.PreviewKeyDown += Datagrid_PreviewKeyDown;
and Methods
private void Datagrid_PreviewKeyDown(object sender, KeyEventArgs e)
{
if (e.Key == Key.RightCtrl||e.Key==Key.LeftCtrl)
{
e.Handled = true;
}
}

Add remove partial view dynamically Prism

I meant to use partial views in the past but never did. Now I do have a need but for some reasons I cannot get it right. I build a noddy project
2 Partial Views
Add and remove each partial views.
Problem.
I seem to be able to Add/remove View first time ,second time round I cant.
I guess I am missing something but not sure what?
I cannot seem to be able to add "x:Name" in code.
Any suggstions?
thanks
Xaml
<StackLayout x:Name="MyStacklayout" HorizontalOptions="Center" VerticalOptions="Center">
<Button x:Name="AddViewOne" Clicked="AddViewOne_OnClicked" Text="Add View One"></Button>
<Button x:Name="AddViewTwo" Clicked="AddViewTwo_OnClicked" Text="Add View Two"></Button>
<Button x:Name="RemoveViewOne" Clicked="RemoveViewOne_OnClicked" Text="Remove View One"></Button>
<Button x:Name="RemoveViewTwo" Clicked="RemoveViewTwo_OnClicked" Text="Remove View Two"></Button>
<views:PartialViewOne x:Name="ViewOne" mvvm:ViewModelLocator.AutowirePartialView="{x:Reference self}" />
<views:PartialViewTwo x:Name="ViewTwo" mvvm:ViewModelLocator.AutowirePartialView="{x:Reference self}" />
</StackLayout>
public partial class MainPage : ContentPage
{
public MainPage()
{
InitializeComponent();
}
private void AddViewOne_OnClicked(object sender, EventArgs e)
{
var myView = new PartialViewOne();
ViewModelLocator.SetAutowirePartialView(myView, this);
MyStacklayout.Children.Add(myView);
}
private void AddViewTwo_OnClicked(object sender, EventArgs e)
{
PartialViewTwo myView = new PartialViewTwo();
ViewModelLocator.SetAutowirePartialView(myView, this);
MyStacklayout.Children.Add(myView);
}
private void RemoveViewOne_OnClicked(object sender, EventArgs e)
{
var viewOneParent = (ViewOne.Parent as Layout<View>);
viewOneParent?.Children.Remove(ViewOne);
}
private void RemoveViewTwo_OnClicked(object sender, EventArgs e)
{
var viewOneParent = (ViewTwo.Parent as Layout<View>);
viewOneParent?.Children.Remove(ViewTwo);
}
}

Flexlayout does not work with command and command parameter

I am being trying to use the flexlayout and it's great but now for each item in the stack layout i need to add tapped gesturer,command etc..
Whatever I try does not work.The only thing that works is the stacklayout.TapGestureRecognizer but as soon as I try to use the command does not work.
I even tried the https://taubensee.net/adding-touch-events-to-flexlayouts/
and added a commandparameter bindable property but does not work either.
How do you add a command with commandparameter to flexlayout .below is my code
<FlexLayout BindableLayout.ItemsSource="{Binding Customers}"
AlignContent="Start"
AlignItems="Start"
Direction="Row"
JustifyContent="Start"
Wrap="Wrap">
<BindableLayout.ItemTemplate>
<DataTemplate>
<StackLayout
FlexLayout.AlignSelf="Start"
FlexLayout.Basis="50%">
<!--<StackLayout.GestureRecognizers>
<TapGestureRecognizer Tapped="TapGestureRecognizer_Tapped" />
</StackLayout.GestureRecognizers>-->
<Frame>
<!--<Frame.GestureRecognizers>
<TapGestureRecognizer
Command="{Binding ItemTappedCommand}"
CommandParameter="{Binding .}" />
</Frame.GestureRecognizers>-->
<StackLayout>
<Label Text="whatever"></Label>
<!--<Image Source="myimage.png">
<Image.GestureRecognizers>
<TapGestureRecognizer Command="{Binding ItemTappedCommand}" CommandParameter="AAAA"
NumberOfTapsRequired="1"></TapGestureRecognizer>
</Image.GestureRecognizers>
</Image>-->
</StackLayout>
</Frame>
</StackLayout>
</DataTemplate>
</BindableLayout.ItemTemplate>
<!--<FlexLayout.Behaviors>
<behaviors:FlexLayoutItemTappedBehavior
Command="{Binding ItemTappedCommand2}" CommandParameter="{Binding .}"/>
</FlexLayout.Behaviors>-->
</FlexLayout>
FlexLayout maybe miss the touch events and commands , have a try with Behavior to realize it.
Create a new class that inherits from Behavior<T>:
public class FlexLayoutItemTappedBehavior : Behavior<FlexLayout>
{
public static readonly BindableProperty CommandProperty =
BindableProperty.Create(nameof(Command), typeof(ICommand), typeof(FlexLayoutItemTappedBehavior), defaultBindingMode: BindingMode.OneWay);
public static readonly BindableProperty ParamaterProperty =
BindableProperty.Create(nameof(Paramater), typeof(object), typeof(FlexLayoutItemTappedBehavior), defaultBindingMode: BindingMode.OneWay);
public ICommand Command
{
get => (ICommand)this.GetValue(CommandProperty);
set => this.SetValue(CommandProperty, value);
}
public object Paramater
{
get => (object)this.GetValue(ParamaterProperty);
set => this.SetValue(ParamaterProperty, value);
}
protected override void OnAttachedTo(FlexLayout bindable)
{
base.OnAttachedTo(bindable);
if (bindable.BindingContext != null)
{
this.BindingContext = bindable.BindingContext;
}
bindable.BindingContextChanged += this.OnFlexLayoutBindingChanged;
bindable.ChildAdded += this.OnFlexLayoutChildAdded;
}
protected override void OnDetachingFrom(FlexLayout bindable)
{
base.OnDetachingFrom(bindable);
bindable.BindingContextChanged -= this.OnFlexLayoutBindingChanged;
bindable.ChildAdded -= this.OnFlexLayoutChildAdded;
foreach (var child in bindable.Children)
{
if (child is View childView && childView.GestureRecognizers.Any())
{
var tappedGestureRecognizers = childView.GestureRecognizers.Where(x => x is TapGestureRecognizer).Cast<TapGestureRecognizer>();
foreach (var tapGestureRecognizer in tappedGestureRecognizers)
{
tapGestureRecognizer.Tapped -= this.OnItemTapped;
childView.GestureRecognizers.Remove(tapGestureRecognizer);
}
}
}
}
private void OnFlexLayoutBindingChanged(object sender, EventArgs e)
{
if (sender is FlexLayout flexLayout)
{
this.BindingContext = flexLayout.BindingContext;
}
}
private void OnFlexLayoutChildAdded(object sender, ElementEventArgs args)
{
if (args.Element is View view)
{
var tappedGestureRecognizer = new TapGestureRecognizer();
tappedGestureRecognizer.Tapped += this.OnItemTapped;
view.GestureRecognizers.Add(tappedGestureRecognizer);
}
}
private async void OnItemTapped(object sender, EventArgs e)
{
if (sender is VisualElement visualElement)
{
var animations = new List<AnimationBase>();
var scaleIn = new ScaleToAnimation
{
Target = visualElement,
Scale = .95,
Duration = "50"
};
animations.Add(scaleIn);
var scaleOut = new ScaleToAnimation
{
Target = visualElement,
Scale = 1,
Duration = "50"
};
animations.Add(scaleOut);
var storyBoard = new StoryBoard(animations);
await storyBoard.Begin();
}
if (sender is BindableObject bindable && this.Command != null && this.Command.CanExecute(null))
{
object resolvedParameter;
if (Paramater != null)
{
resolvedParameter = Paramater;
}
else
{
resolvedParameter = e;
}
if (Command.CanExecute(resolvedParameter))
{
this.Command.Execute(bindable.BindingContext);
}
}
}
}
Lastly, in order to use this Behavior from XAML, you can reference it like this:
<FlexLayout.Behaviors>
<behaviors:FlexLayoutItemTappedBehavior
Command="{Binding NavigateToDetailCommand}" Paramater="{Binding .}"/>
</FlexLayout.Behaviors>
About Reusable EventToCommandBehavior , you can refer to here .

Can I hide/show asp:Menu items based on role?

Am I able to hide certain menu items in an asp:Menu control based on role?
<asp:Menu ID="mTopMenu" runat="server" Orientation="Horizontal" />
<Items>
<asp:MenuItem Text="File">
<asp:MenuItem Text="New Project" />
<asp:MenuItem Text="Release Template" NavigateUrl="~/Release/ReleaseTemplate.aspx" />
<asp:MenuItem Text="Release Schedule" NavigateUrl="~/Release/ReleaseSchedule.aspx" />
<asp:MenuItem Text="Roles" NavigateUrl="~/Admin/AdminRoles.aspx" />
</asp:MenuItem>
</Items>
</asp:Menu>
How can I make one of these items visible to only users in the Admin role?
I am using asp.net role provider.
You can remove unwanted menu items in Page_Load, like this:
protected void Page_Load(object sender, EventArgs e)
{
if (!Roles.IsUserInRole("Admin"))
{
MenuItemCollection menuItems = mTopMenu.Items;
MenuItem adminItem = new MenuItem();
foreach (MenuItem menuItem in menuItems)
{
if (menuItem.Text == "Roles")
adminItem = menuItem;
}
menuItems.Remove(adminItem);
}
}
I'm sure there's a neater way to find the right item to remove, but this one works. You could also add all the wanted menu items in a Page_Load method, instead of adding them in the markup.
You can bind the menu items to a site map and use the roles attribute. You will need to enable Security Trimming in your Web.Config to do this. This is the simplest way.
Site Navigation Overview:
http://msdn.microsoft.com/en-us/library/e468hxky.aspx
Security Trimming Info:
http://msdn.microsoft.com/en-us/library/ms178428.aspx
SiteMap Binding Info:
http://www.w3schools.com/aspnet/aspnet_navigation.asp
Good Tutorial/Overview here:
http://weblogs.asp.net/jgalloway/archive/2008/01/26/asp-net-menu-and-sitemap-security-trimming-plus-a-trick-for-when-your-menu-and-security-don-t-match-up.aspx
Another option that works, but is less ideal is to use the loginview control which can display controls based on role. This might be the quickest (but least flexible/performant) option. You can find a guide here: http://weblogs.asp.net/sukumarraju/archive/2010/07/28/role-based-authorization-using-loginview-control.aspx
I prefer to use the FindItem method and use the value path for locating the item. Make sure your PathSeparator property on the menu matches what you're using in FindItem parameter.
protected void Page_Load(object sender, EventArgs e)
{
// remove manage user accounts menu item for non-admin users.
if (!Page.User.IsInRole("Admin"))
{
MenuItem item = NavigationMenu.FindItem("Users/Manage Accounts");
item.Parent.ChildItems.Remove(item);
}
}
This is best done in the MenuItemDataBound.
protected void NavigationMenu_MenuItemDataBound(object sender, MenuEventArgs e)
{
if (!Page.User.IsInRole("Admin"))
{
if (e.Item.NavigateUrl.Equals("/admin"))
{
if (e.Item.Parent != null)
{
MenuItem menu = e.Item.Parent;
menu.ChildItems.Remove(e.Item);
}
else
{
Menu menu = (Menu)sender;
menu.Items.Remove(e.Item);
}
}
}
}
Because the example used the NavigateUrl it is not language specific and works on sites with localized site maps.
SIMPLE method may not be the best for all cases
<%
if (Session["Utype"].ToString() == "1")
{
%>
<li><i class="fa fa-users"></i><span>STUDENT DETAILS</span></li>
<li><i class="fa fa-user-plus"></i><span>USER MANAGEMENT</span></li>
<%
}
else
{
%>
<li><i class="fa fa-object-group"></i><span>PACKAGE</span></li>
<%
}
%>
Try this:
protected void Menu1_DataBound(object sender, EventArgs e)
{
recursiveMenuVisit(Menu1.Items);
}
private void recursiveMenuVisit(MenuItemCollection items)
{
MenuItem[] itemsToRemove = new MenuItem[items.Count];
int i = 0;
foreach (MenuItem item in items)
{
if (item.NavigateUrl.Contains("Contact.aspx"))
{
itemsToRemove[i] = item;
i++;
}
else
{
if (item.ChildItems.Count > 0) recursiveMenuVisit(item.ChildItems);
}
}
for(int j=0; j < i; j++)
{
items.Remove(itemsToRemove[j]);
}
}
I have my menu in the site master page. I used the Page_Load() function to make the "Admin" menu item only visible to users with an Admin role.
using System;
using System.Linq;
using Telerik.Web.UI;
using System.Web.Security;
<telerik:RadMenu ID="menu" runat="server" RenderMode="Auto" >
<Items>
<telerik:RadMenuItem Text="Admin" Visible="true" />
</Items>
</telerik:RadMenu>
protected void Page_Load(object sender, EventArgs e)
{
if (!IsPostBack)
{
RadMenuItem item = this.menu.FindItemByText("Admin");
if (null != item)
{
if (Roles.IsUserInRole("Admin"))
{
item.Visible = true;
}
else
{
item.Visible = false;
}
}
}
}
To remove a MenuItem from an ASP.net NavigationMenu by Value:
public static void RemoveMenuItemByValue(MenuItemCollection items, String value)
{
MenuItem itemToRemove = null;
//Breadth first, look in the collection
foreach (MenuItem item in items)
{
if (item.Value == value)
{
itemToRemove = item;
break;
}
}
if (itemToRemove != null)
{
items.Remove(itemToRemove);
return;
}
//Search children
foreach (MenuItem item in items)
{
RemoveMenuItemByValue(item.ChildItems, value);
}
}
and helper extension:
public static RemoveMenuItemByValue(this NavigationMenu menu, String value)
{
RemoveMenuItemByValue(menu.Items, value);
}
and sample usage:
navigationMenu.RemoveMenuItemByValue("UnitTests");
Note: Any code is released into the public domain. No attribution required.
You just have to remove the parent menu in the page init event.
Protected Sub navMenu_Init(sender As Object, e As System.EventArgs) Handles navMenu.Init
'Remove the admin menu for the norms
Dim cUser As Boolean = HttpContext.Current.User.IsInRole("Admin")
'If user is not in the Admin role removes the 1st menu at index 0
If cUser = False Then
navMenu.Items.RemoveAt(0)
End If
End Sub
To find menu items in content page base on roles
protected void Page_Load(object sender, EventArgs e)
{
if (Session["AdminSuccess"] != null)
{
Menu mainMenu = (Menu)Page.Master.FindControl("NavigationMenu");
//you must know the index of items to be removed first
mainMenu.Items.RemoveAt(1);
//or you try to hide menu and list items inside menu with css
// cssclass must be defined in style tag in .aspx page
mainMenu.CssClass = ".hide";
}
}
<style type="text/css">
.hide
{
visibility: hidden;
}
</style>

Resources