Creating UI element at runtime - c#

I need to create some buttons at runtime. I tried to find a solution online, but there were only old threads. The only thing I've found is the following code:
ViewGroup layout = (ViewGroup)Resource.Layout.Main;
Button btn = new Button(this);
btn.Text = "text";
btn.LayoutParameters = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MatchParent, ViewGroup.LayoutParams.WrapContent);
layout.AddView(btn);
I don't understand how this should work. There are no compile errors, but the app closes instantly after launching. Can you explain why this happens and how to write this code properly?

Doing
ViewGroup layout = (ViewGroup)Resource.Layout.Main;
Won't give you the currently inflated layout. Resource.Layout.Main is just an int pointing at a resource.
Instead give your currently shown layout and id:
android:id="#+id/root"
Now it does matter what type it is. The default templates use LinearLayout. So finding it would be:
var root = FindViewById<LinearLayout>(Resource.Id.root);
Then you can add your button to that:
var button = new Button(this)
{
Text = "hello",
LayoutParameters = new LinearLayout.LayoutParams(
ViewGroup.LayoutParams.MatchParent,
ViewGroup.LayoutParams.WrapContent)
};
root.AddView(button);

Related

Adding and Opening an DialogPage (or window) for MacOS (C#)

I'm very new at programming in C# and Xamarin, and now I'm stuck.
I made a menu with some help, now I can do Cut/Copy/Paste which works fine, but now I want to make an Dialog or Window for some User Settings whenever they click on Settings in the menu. (I've made the menu in AppDelegate.cs)
I've look around a lot on Google and Xamarin forums, but al they talk about is to add a new kind of storyboard thingy. Which I guess will work, but I'm not using any storyboard (Just because we want to make everything out of code).
Found on Xamarin:
https://developer.xamarin.com/guides/mac/user-interface/working-with-dialogs/
And then the Preference window (That is what I prefer).
Code I made in AppDelegate.cs (Menu button which has to open an Window or Dialog):
var prefMenuItem = new NSMenuItem("Settings", ",", handler: delegate
{
var prefStyle = NSWindowStyle.Closable | NSWindowStyle.Resizable | NSWindowStyle.Titled;
var rect = new CoreGraphics.CGRect(200, 1000, 1024, 768);
prefWindow = new NSWindow(rect, prefStyle, NSBackingStore.Buffered, false);
prefWindow.Title = "Settings";
prefWindow.TitleVisibility = NSWindowTitleVisibility.Hidden;
});
'Settings' Button in the menu is visible btw.
Probably I forgot something, it won't open a thing.
How can I make this work?
Maybe this will work for you.
var prefMenuItem = new NSMenuItem("Settings", ",", handler: delegate
{
var prefStyle = NSWindowStyle.Closable | NSWindowStyle.Resizable | NSWindowStyle.Titled;
var rect = new CoreGraphics.CGRect(200, 1000, 1024, 768);
prefWindow = new NSWindow(rect, prefStyle, NSBackingStore.Buffered, false);
prefWindow.Title = "Settings";
prefWindow.TitleVisibility = NSWindowTitleVisibility.Hidden;
NSApplication.SharedApplication.RunModalForWindow(prefWindow);
});
I added NSApplication.SharedApplication.RunModalForWindow(prefWindow); in your code. This will open a new window.
Found this on: https://forums.xamarin.com/discussion/22729/xamarin-mac-how-to-set-position-of-modal-window

Xamarin.Forms: Rendering Xamarin.Froms Control in native Dialog - wrap_content ignored

Say I want to render a Xamarin.Froms control (e.g a Xamarin.Forms.Label) in a native android view.
The following code works. But ignores the width and height of the control (wrap_content).
// create a Xamarin.Froms.Label
Xamarin.Forms.Label view = new Xamarin.Forms.Label()
{
Text = "This is a Xamarin.Forms.Label",
BackgroundColor = Color.Red,
};
// create a Renderer
var renderer = Xamarin.Forms.Platform.Android.Platform.CreateRenderer(view);
// create an AlertDialog
var builder = new AlertDialog.Builder(Xamarin.Forms.Forms.Context);
builder.SetView(renderer.ViewGroup); // Use the ViewGroup of the renderer
builder.SetTitle("Dialog");
// create and show the dialog
builder.Create().Show();
Produces the following:
Doing the same with an native label:
var view = new Android.Widget.TextView(Xamarin.Forms.Forms.Context)
{
Text = "This is a Android.Widget.TextView",
Background = new Android.Graphics.Drawables.ColorDrawable(Android.Graphics.Color.Red),
};
var builder = new AlertDialog.Builder(Xamarin.Forms.Forms.Context);
builder.SetView(view);
builder.SetTitle("Dialog");
builder.Create().Show();
Produces the expected output:
I am looking for any hints or advices regarding width / height measurement in Xamarin.Forms, and/or a solution for this kind of problem.
Update
Also note that this does not depend on the AlertDialog, using a LinearLayout instead of a AlertDialog results to the same behaviour.
The following code works. But ignores the width and height of the control (wrap_content).
Reason:
You passed a ViewGroup to the AlertDialog. But inside the AlertDialog the layout params of ViewGroup won't work at all. Refering to this blog:
Every Rule Has An Exception
...
The issue here is that AlertDialog.Builder supports a custom view, but does not provide an implementation of setView() that takes a layout resource; so you must inflate the XML manually. However, because the result will go into the dialog, which does not expose its root view (in fact, it doesn’t exist yet), we do not have access to the eventual parent of the layout, so we cannot use it for inflation. It turns out, this is irrelevant, because AlertDialog will erase any LayoutParams on the layout anyway and replace them with match_parent.
So instead of passing a ViewGroup, pass a view to AlertDialog like your second try.
Update(a few more research):
if try the following codes to set the height of ViewGroup to 400:
// create a Renderer
var renderer = Xamarin.Forms.Platform.Android.Platform.CreateRenderer(view);
// create an AlertDialog
var builder = new AlertDialog.Builder(Xamarin.Forms.Forms.Context);
renderer.ViewGroup.LayoutParameters = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.WrapContent, 400);
builder.SetView(renderer.ViewGroup); // Use the ViewGroup of the renderer
builder.SetTitle("Dialog");
// create and show the dialog
builder.Create().Show();
It doesn't work, the dialog is still almost covers the whole screen:
But if add the renderered ViewGroup to LinearLayout like this:
var renderer = Xamarin.Forms.Platform.Android.Platform.CreateRenderer(view);
//The following codes works correctly
renderer.ViewGroup.LayoutParameters = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.WrapContent, 400);
LinearLayout linearLayout = new LinearLayout(Xamarin.Forms.Forms.Context);
linearLayout.LayoutParameters = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MatchParent, ViewGroup.LayoutParams.WrapContent);
linearLayout.AddView(renderer.ViewGroup);
this.AddContentView(linearLayout,new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MatchParent, ViewGroup.LayoutParams.WrapContent));
The height was set correctly:
So, on this layer, the problem lies in the AlertDialog as I mentioned above.
But If the height was set to Wrap_content like this:
var renderer = Xamarin.Forms.Platform.Android.Platform.CreateRenderer(view);
//set the height again to wrap_content
renderer.ViewGroup.LayoutParameters = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.WrapContent, ViewGroup.LayoutParams.WrapContent);
LinearLayout linearLayout = new LinearLayout(this);
linearLayout.LayoutParameters = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MatchParent, ViewGroup.LayoutParams.WrapContent);
linearLayout.AddView(renderer.ViewGroup);
this.AddContentView(linearLayout,new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MatchParent, ViewGroup.LayoutParams.WrapContent));
the rendererd ViewGroup again start to fill the whole screen:
On this layer I found that the problem lies in the rendered ViewGroup, because after changing the ViewGroup to a native LinearLayout everything is fine.
I checked the Source codes of Xamarin.Forms, and found the following Inherit hierarchy:
Xamarin.Forms.Platform.AndroidLabelRenderer ->
Xamarin.Forms.Platform.Android.ViewRenderer<TView, TNativeView> ->
Xamarin.Forms.Platform.Android.VisualElementRenderer<TElement> ->
Xamarin.Forms.Platform.Android.FormsViewGroup ->
Android.Views.ViewGroup
I found none of these classes has implemented ViewGroup.OnMeasure method. Thus the wrap_content will never work. You can refer to Custom ViewGroup for an detailed implementation example of custom ViewGroup.
I answer my own question since It is probalby the easiest way to solve the problem:
Since Xamarin.Forms 2.3.5 (as time of writing in prerelease) there is a new Property View next to ViewGroup in IVisualElementRenderer.
The solution would be to use Xamarin.Froms 2.5.3 and the property View:
Xamarin.Forms.Label view = new Xamarin.Forms.Label()
{
Text = "This is a Xamarin.Forms.Label",
BackgroundColor = Color.Red,
};
var renderer = Xamarin.Forms.Platform.Android.Platform.CreateRenderer(view);
var builder = new AlertDialog.Builder(Xamarin.Forms.Forms.Context);
builder.SetView(renderer.View); // Use View instead of ViewGroup
builder.SetTitle("Dialog");
builder.Create().Show();

DevExpress DropDownButton Problems

I'm trying to create a DevEx drop down button. Unfortunately, I'm running into two problems I can't figure out:
1) I can't get the popup menu to skin correctly, i.e. it doesn't skin as "Office 2010 Blue". The code I'm using is shown below:
private void InitializeSendToPricingSheetButton()
{
var barManager = new BarManager();
if (barManager.Controller == null) barManager.Controller = new BarAndDockingController();
barManager.Controller.PaintStyleName = "Skin";
barManager.Controller.LookAndFeel.UseDefaultLookAndFeel = false;
barManager.Controller.LookAndFeel.SkinName = "Office 2010 Blue";
barManager.ItemClick += HandleSendToPricingSheetClick;
barManager.Items.AddRange(new[] { new BarButtonItem(barManager, "Foo"), new BarButtonItem(barManager, "Bar"), new BarButtonItem(barManager, "Baz") });
var popupMenu = new PopupMenu { Manager = barManager };
foreach (var barItem in barManager.Items) popupMenu.ItemLinks.Add((BarItem)barItem);
popupMenu.ItemLinks[1].BeginGroup = true;
dropDownButtonSendToPricingSheet.DropDownControl = popupMenu;
}
2) This button is on a form. If the form loses focus (e.g. I click on Firefox), the pop-up menu still remains on-top. It won't go away until clicked.
Any suggestions would be much appreciated. Thanks for helping me deal with DevEx insanity.
I have solution to your second question.
You should add drop down button event handler as below:
dropDownButton1.LostFocus += new EventHandler(HidePopUp);
Handler method should be as below:
private void HidePopUp(object sender,object e)
{
dropDownButton1.HideDropDown();
}
For your second question, you should assign value to the bar manager property as:
BarManager manager = new BarManager();
manager.Form = this; // refers to current form
Find below link for reference
https://www.devexpress.com/Support/Center/Question/Details/Q274641
It is probably simpler to use DefaultLookAndFeel
Add this comp to your form and set the theme you'd like to use.
There is no need to set the theme for individual components.
defaultLookAndFeel1.LookAndFeel.SetSkinStyle("Office 2010 Blue");

TreeListControl within each NavbarControl Group

I'm very new to WPF and I'm attempting to create a treelist navigation within each navbar group. Because the number of navbar groups and treelists are dynamic I have to make them in code rather than them be pre-defined in XAML.
I have tested the following so far which is meant to define the navbar group's content rather than use the default item
private void CreateGroup2(NavBarControl navBar)
{
NavBarGroup group2 = new NavBarGroup();
group2.Header = "Custom Content";
//Specify that the group's content should be defined via the Content property
group2.DisplaySource = DisplaySource.Content;
TreeListControl tree = new TreeListControl();
tree.ItemsSource = TreeList_DataBinding.Stuff.GetStuff();
group2.Content = tree;
navBar.Groups.Add(group2);
}
This gives an Exception: Grid.InfiniteGridSizeException: By default, an infinite grid height is not allowed since all grid rows will be rendered and hence the grid will work very slowly. To fix this issue, you should place the grid into a container that will give a finite height to the grid, or you should manually specify the grid's Height or MaxHeight. Note that you can also avoid this exception by setting the TreeListControl.AllowInfiniteGridSize static property to True, but in that case the grid will run slowly.
I'm a little confused as I'm not using a grid? Can anyone give any pointers what's wrong and how I can add a treview under each navbar group?
Thank You
It feels a bit wrong answering my own question but I managed to get it working using the following
private void CreateGroup2(NavBarGroup navBarGroup)
{
System.Windows.Controls.TreeView treeview = new System.Windows.Controls.TreeView();
TreeViewItem nod = new TreeViewItem();
nod.Header = "Tree Node1";
treeview.Items.Add(nod);
TreeViewItem nod1 = new TreeViewItem();
nod1.Header = "Tree Node2";
treeview.Items.Add(nod1);
TreeViewItem nod2 = new TreeViewItem();
nod2.Header = "Tree Node3";
nod1.Items.Add(nod2);
//StackPanel stcPnl = new StackPanel(); /optiona
//stcPnl.Children.Add(treeview);
//navBarGroup.Content = stcPnl;
navBarGroup.Content = treeview;
navBarGroup.DisplaySource = DisplaySource.Content;
}

FindName returning null

I'm writing a simple tic tac toe game for school. The assignment is in C++, but the teacher has given me permission to use C# and WPF as a challenge. I've gotten all the game logic finished and the form mostly complete, but I've run into a wall. I'm currently using a Label to indicate who's turn it is, and I want to change it when a player makes a valid move. According to Applications = Code + Markup, I should be able to use the FindName method of the Window class. However, it keeps returning null. Here's the code:
public TicTacToeGame()
{
Title = "TicTacToe";
SizeToContent = SizeToContent.WidthAndHeight;
ResizeMode = ResizeMode.NoResize;
UniformGrid playingField = new UniformGrid();
playingField.Width = 300;
playingField.Height = 300;
playingField.Margin = new Thickness(20);
Label statusDisplay = new Label();
statusDisplay.Content = "X goes first";
statusDisplay.FontSize = 24;
statusDisplay.Name = "StatusDisplay"; // This is the name of the control
statusDisplay.HorizontalAlignment = HorizontalAlignment.Center;
statusDisplay.Margin = new Thickness(20);
StackPanel layout = new StackPanel();
layout.Children.Add(playingField);
layout.Children.Add(statusDisplay);
Content = layout;
for (int i = 0; i < 9; i++)
{
Button currentButton = new Button();
currentButton.Name = "Space" + i.ToString();
currentButton.FontSize = 32;
currentButton.Click += OnPlayLocationClick;
playingField.Children.Add(currentButton);
}
game = new TicTacToe.GameCore();
}
void OnPlayLocationClick(object sender, RoutedEventArgs args)
{
Button clickedButton = args.Source as Button;
int iButtonNumber = Int32.Parse(clickedButton.Name.Substring(5,1));
int iXPosition = iButtonNumber % 3,
iYPosition = iButtonNumber / 3;
if (game.MoveIsValid(iXPosition, iYPosition) &&
game.Status() == TicTacToe.GameCore.GameStatus.StillGoing)
{
clickedButton.Content =
game.getCurrentPlayer() == TicTacToe.GameCore.Player.X ? "X" : "O";
game.MakeMoveAndChangeTurns(iXPosition, iYPosition);
// And this is where I'm getting it so I can use it.
Label statusDisplay = FindName("StatusDisplay") as Label;
statusDisplay.Content = "It is " +
(game.getCurrentPlayer() == TicTacToe.GameCore.Player.X ? "X" : "O") +
"'s turn";
}
}
What's going on here? I'm using the same name in both places, but FindName can't find it. I've tried using Snoop to see the hierarchy, but the form doesn't show up in the list of applications to choose from. I searched on StackOverflow and found I should be able to use VisualTreeHelper class, but I don't understand how to use it.
Any ideas?
FindName operates on the XAML namescope of the calling control. In your case, since the control is created entirely within code, that XAML namescope is empty -- and that's why FindName fails. See this page:
Any additions to the element tree after initial loading and processing must call the appropriate implementation of RegisterName for the class that defines the XAML namescope. Otherwise, the added object cannot be referenced by name through methods such as FindName. Merely setting a Name property (or x:Name Attribute) does not register that name into any XAML namescope.
The easiest way to fix your problem is to store a reference to your StatusDisplay label in the class as a private member. Or, if you want to learn how to use the VisualTreeHelper class, there's a code snippet at the bottom of this page that walks the visual tree to find the matching element.
(Edited: Of course, it's less work to call RegisterName than to use the VisualTreeHelper, if you don't want to store a reference to the label.)
I'd recommend reading the first link in its entirety if you plan on using WPF/Silverlight in any depth. Useful information to have.
You have to create a new NameScope for your window:
NameScope.SetNameScope(this, new NameScope());
Then you register name of your label with the window:
RegisterName(statusDisplay.Name, statusDisplay);
So this seems to be all you need to do to make FindName() work.

Categories

Resources