Add Control programmatically, UI is not updating - maui

I am just playing arround with .Net MAUI.
I want to retrieve information from a Rest service and based on the result I want to add Buttons programmatically to a VerticalStackLayout.
When I debug the solution the buttons are added to the VerticalStackLayout but the UI is not updated.
Here the code Snippet
var btn = new Button();
btn.Text = "Button " + count + " added";
btn.Clicked += OnCounterClicked;
btn.HorizontalOptions = LayoutOptions.Center;
VLayout.Add(btn);
Here the XAML
<ScrollView>
<VerticalStackLayout x:Name="VLayout"
Spacing="25"
Padding="30,0"
VerticalOptions="Center">
<Image
Source="dotnet_bot.png"
SemanticProperties.Description="Cute dot net bot waving hi to you!"
HeightRequest="200"
HorizontalOptions="Center" />
<Label
Text="Hello, World!"
SemanticProperties.HeadingLevel="Level1"
FontSize="32"
HorizontalOptions="Center" />
<Label
Text="Welcome to .NET Multi-platform App UI"
SemanticProperties.HeadingLevel="Level2"
SemanticProperties.Description="Welcome to dot net Multi platform App U I"
FontSize="18"
HorizontalOptions="Center" />
<Entry x:Name="entry"
Placeholder="Enter text"
TextChanged="OnTextChanged"
Completed="OnTextCompleted" />
<Button
x:Name="KommenBtn"
Text="Kommen"
SemanticProperties.Hint="Counts the number of times you click"
Clicked="OnCounterClicked"
HorizontalOptions="Center" />
<Button
x:Name="GehenBtn"
Text="Gehen"
SemanticProperties.Hint="Counts the number of times you click"
Clicked="OnCounterClicked"
HorizontalOptions="Center" />
</VerticalStackLayout>
</ScrollView>
Thanks in advance for your hints & help!
BW

Try to update UI on main thread , you can wrap your code into Application.Current.Dispatcher.Dispatch .
Sample code
Application.Current.Dispatcher.Dispatch(() =>
{
var btn = new Button();
btn.Text = "Button " + count + " added";
btn.Clicked += OnCounterClicked;
btn.HorizontalOptions = LayoutOptions.Center;
VLayout.Add(btn);
});

To update UI after a change that affects the hierarchy (or positions) of controls:
(VLayout as IView).InvalidateArrange();
NOTE: This is roughly equivalent to Xamarin.Forms layout.ForceLayout();
If not already in code running on UI MainThread, wrap it in Dispatch:
Dispatcher.Dispatch(() =>
(VLayout as IView).InvalidateArrange());

Related

Prevent a Border control from shrinking when its child controls shrink

I have a Border control that contains a couple of Label controls. The text shown in the labels changes, and this causes the Border control to expand and shrink horizontally. To prevent flickering, I want it to be able to expand but not shrink. Is there a way to do this?
Alternatively, is there a way to specify that the Label objects should expand horizontally but never shrink - which would give the same outcome?
My XAML is:
<?xml version="1.0" encoding="utf-8" ?>
<ContentView xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="uk.andyjohnson.TakeoutExtractor.Gui.ProgressOverlay">
<Border
Stroke="#0f0f0f"
StrokeThickness="2"
StrokeShape="RoundRectangle 5,5,5,5"
HorizontalOptions="CenterAndExpand"
VerticalOptions="CenterAndExpand"
Padding="30,30">
<VerticalStackLayout
Spacing="25" >
<Label
x:Name="SourceLabel"
Text=""
HorizontalOptions="Center"/>
<Label
x:Name="DestinationLabel"
Text=""
HorizontalOptions="Center" />
</VerticalStackLayout>
</Border>
</ContentView>
Thanks!
You could play with labels WidthRequest.
When your label increase in size, you set its WidthRequest to its current Width value.
Assuming for simplicity that you're changing the label text in your code-behind and that there is only one label, something like this should do the job.
public partial class ProgressOverlay
{
private double currentWidth = 0;
// I create this method just to explain.
// It could be anything in your code that changes your label text
void ChangingText()
{
// ******
// .... SourceLabel text changes here ...
// *******
if (SourceLabel.Width > currentWidth)
{
SourceLabel.WidthRequest = SourceLabel.Width;
currentWidth = SourceLabel.Width;
}
}
}
I solved the problem using a variation on Riccardo Minato's suggestion.
Hook into the SizeChanged event on the bounding Border object.
Track its maximum width using a member variable
When the width exceeds the previous maximum, set the Border object's MinimumWidthRequest property to the new maximum width. This stops it shrinking.
New XAML:
<?xml version="1.0" encoding="utf-8" ?>
<ContentView xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="uk.andyjohnson.TakeoutExtractor.Gui.ProgressOverlay">
<Border
Stroke="#0f0f0f"
StrokeThickness="2"
StrokeShape="RoundRectangle 5,5,5,5"
HorizontalOptions="CenterAndExpand"
VerticalOptions="CenterAndExpand"
Padding="30,30"
SizeChanged="Border_SizeChanged"> <!-- ** Added this line ** -->
<VerticalStackLayout
Spacing="25" >
<ActivityIndicator
IsVisible="true"
IsRunning="true"
IsEnabled="true"
HorizontalOptions="Center"/>
<Button
x:Name="CancelButton"
Text="Cancel"
HorizontalOptions="Center"
Clicked="OnCancelButtonClicked"/>
<Label
x:Name="SourceLabel"
Text=""
HorizontalOptions="Center"/>
<Label
x:Name="DestinationLabel"
Text=""
HorizontalOptions="Center"/>
</VerticalStackLayout>
</Border>
</ContentView>
New code-behind:
private double currentMaxWidth = 0D;
private void Border_SizeChanged(object sender, EventArgs e)
{
var bdr = sender as Border;
if (bdr.Width > currentMaxWidth)
{
currentMaxWidth = bdr.Width;
bdr.MinimumWidthRequest = currentMaxWidth;
}
}

The scrollToSection method does not scroll

I have a ObjectPageLayout that is divided two sections, that it looks like:
<Page id="floatingFooterPage" enableScrolling="false" showNavButton="true" navButtonPress="onNavButtonPress">
<content>
<uxap:ObjectPageLayout id="ClassDetail" showHeaderContent="true">
<uxap:headerTitle>
<uxap:ObjectPageHeader></uxap:ObjectPageHeader>
</uxap:headerTitle>
<uxap:headerContent>
<f:SimpleForm editable="false" layout="ResponsiveGridLayout" singleContainerFullSize="false">
<f:content>
<Label text="{i18n>charsClass}"/>
<Text text="{ClassInfo>/classNum} {ClassInfo>/classNumDescr}"/>
<Label text="{i18n>charsClassType}"/>
<Text text="{ClassInfo>/classType} {ClassInfo>/classTypeText}"/>
</f:content>
</f:SimpleForm>
</uxap:headerContent>
<uxap:sections>
<uxap:ObjectPageSection title="{i18n>charsCharsSel}" id="SecChars">
<uxap:subSections>
<uxap:ObjectPageSubSection >
<uxap:blocks>
<mvc:XMLView width="100%" viewName="ch.mindustrie.ZMM_OBJECTS_CLASSES.view.CharacSelection" id="CharacSelection"/>
</uxap:blocks>
</uxap:ObjectPageSubSection>
</uxap:subSections>
</uxap:ObjectPageSection>
<uxap:ObjectPageSection title="{i18n>charsObject}" id="SecObject">
<uxap:subSections>
<uxap:ObjectPageSubSection >
<uxap:blocks>
<mvc:XMLView width="100%" viewName="ch.mindustrie.ZMM_OBJECTS_CLASSES.view.ObjectTable" id="ObjectTable"/>
</uxap:blocks>
</uxap:ObjectPageSubSection>
</uxap:subSections>
</uxap:ObjectPageSection>
<!-- <uxap:ObjectPageSection title="Sub classes" id="SecSub" visible="false">
<uxap:subSections>
<uxap:ObjectPageSubSection >
<uxap:blocks>
<mvc:XMLView width="100%" viewName="ch.mindustrie.ZMM_OBJECTS_CLASSES.view.ClassTree" id="SubTree"/>
</uxap:blocks>
</uxap:ObjectPageSubSection>
</uxap:subSections>
</uxap:ObjectPageSection>-->
</uxap:sections>
</uxap:ObjectPageLayout>
</content>
<footer>
<Toolbar>
<ToolbarSpacer/>
<Button id="FindObject" text="{i18n>charsObject}" press="onPress" type="Transparent"/>
</Toolbar>
</footer>
</Page>
I wanted to scroll to section SecChars programmatically and I have done as following:
_scrollToObjectSection: function () {
const oObjPage = this.byId("ClassDetail");
oObjPage.scrollToSection("SecObject", 0, 0);
}
But it does not work at all. What am I doing wrong?
Issues
The API scrollToSection awaits a global ID (the suffix "SecObject" alone is not enough). So it should be:
oObjPage.scrollToSection(this.byId("SecObject").getId());
That API, however, can be only successfully performed when the DOM of the ObjectPageLayout is fully ready. I added "fully" because calling scrollToSection within the onAfterRendering event delegate won't do anything either. There must be an additional timeout of about 450 ms. It's nowhere documented though how many milliseconds are actually required, and there is no public event for this case either.
Alternatives
Use setSelectedSection instead, as mentioned in the documentation topic Object Page Scrolling. The ObjectPageLayout will scroll to that section, but currently without any scrolling animation (iDuration = 0). The "selectedSection" is an association, hence, it can be also set within the view definition:
<uxap:ObjectPageLayout selectedSection="mySection">
<uxap:ObjectPageSection id="mySection">
Keep using scrollToSection but leverage the ⚠️ private event "onAfterRenderingDOMReady"(src) which is fired when the DOM is fully ready:
onInit: function() {
// ...
this.scrollTo(this.byId("mySection"), this.byId("myObjectPageLayout"));
},
scrollTo: function(section, opl) {
const id = section.getId();
const ready = !!opl.getScrollingSectionId(); // DOM of opl is fully ready
const fn = () => opl.scrollToSection(id);
return ready ? fn() : opl.attachEventOnce("onAfterRenderingDOMReady", fn);
},
The API getScrollingSectionId() returns a falsy value (currently "") if the DOM is not fully ready.

Data Binding from TableSelectDialog to Form

I'm using TableSelectDialog to view some Carrier details. All I need is: If I select any item (row), then all the values from that row should get automatically populated to the form input fields.
In the attached image, if Carrier Name is selected from TableSelectDialog, then the remaining fields should be populated based on that value.
You can achieve the required functionality using the Binding context that you receive from the TableSelcect Dialog.
But for binding context to work properly, both form and the table select dialog should refer to the same Model.
Below is the working code:
VIEW.XML:
Has a Button to trigger the table select dialog.
Has a form.
<l:VerticalLayout class="sapUiContentPadding" width="100%">
<l:content>
<Button class="sapUiSmallMarginBottom" text="Show Table Select Dialog"
press="handleTableSelectDialogPress">
</Button>
<VBox class="sapUiSmallMargin">
<f:SimpleForm id="SimpleFormDisplay354">
<f:content>
<Label text="Supplier Name" />
<Text id="nameText" text="{SupplierName}" />
<Label text="Description" />
<Text text="{Description}" />
<Label text="ProductId" />
<Text text="{ProductId}" />
<Label text="Quantity" />
<Text id="countryText" text="{Quantity}" />
</f:content>
</f:SimpleForm>
</VBox>
</l:content>
</l:VerticalLayout>
Controller:
onInit: function () {
// set explored app's demo model on this sample
var oModel = new JSONModel(jQuery.sap.getModulePath("sap.ui.demo.mock", "/products.json"));
this.getView().setModel(oModel);
},
handleTableSelectDialogPress: function (oEvent) {
if (!this._oDialog) {
this._oDialog = sap.ui.xmlfragment("sap.m.sample.TableSelectDialog.Dialog", this);
}
this.getView().addDependent(this._oDialog);
// toggle compact style
this._oDialog.open();
},
handleClose: function (oEvent) {
var aContexts = oEvent.getParameter("selectedContexts");
if (aContexts && aContexts.length) {
// MessageToast.show("You have chosen " + aContexts.map(function(oContext) { return oContext.getObject().Name; }).join(", "));
this.byId('SimpleFormDisplay354').setBindingContext(aContexts[0]);
}
oEvent.getSource().getBinding("items").filter([]);
}
Now, we also have a Select dialog and on click of any Item we call method : handleClose
In handleClose, we obtain the clicked Item binding context and then tell the form : hey! refer to this context ( which is present in the model). Binding context has a path which tells the form from where to do the relative binding.
Please feel free to contact for more information.

How to create a grid view with two columns per row using RadListView

I thought this is easy, but unfortunately, I can not find out any answer.
I installed telerik-ui, and want to use RadListView. From the doc, I can see the demo page with perfect layout, to have mulitple items per row. In my case, I want to have two columns per row and then show the list view. Below is my xml code, but it is totally blank, I do not know why. Can you help?
This is the code in my XML:
<RadListView [items]="flattenDishes">
<ListViewLinearLayout scrollDirection="Vertical" itemHeight="500" spanCount="2"></ListViewLinearLayout>
<ng-template tkListItemTemplate let-flattenDish="item">
<GridLayout rows="auto" columns="auto, *">
<StackLayout orientation="vertical">
<Image [src]="serverUrl +'images/' + flattenDish.get('option_imageUrl') " stretch="aspectFill" width=200 height=200></Image>
<Label [text]="flattenDish.get('option_size')+':'+ flattenDish.getTrans('zh').get('title')"></Label>
<Label [text]="flattenDish.get('option_price')"></Label>
</StackLayout>
</GridLayout>
</ng-template>
</RadListView>
Can anyone help me to have a working template here?
Thanks
Try this, is not with RadListView but the important part is inside the item:
<ListView class="list-group" [items]="workOrders" (itemTap)="onItemTapSecondList($event)" separatorColor="gray">
<ng-template let-workOrder="item">
<StackLayout orientation="horizontal">
<StackLayout>
<Label text="Order ref:" class="order-id-label"></Label>
</StackLayout>
<StackLayout>
<Label text="Order date:" class="order-id-label"></Label>
</StackLayout>
</StackLAyout>
</ng-template>
</ListView>

How to add views to the steps of a roadmap in sapui5

I am looking to add views (or workflow) to a roadmap steps in sapui5. I am new to sapui5, can any one help me out with this?
My code:
<script>
//create the RoadMap control
var oRMap = new sap.ui.commons.RoadMap("rMap");
//create the RoadMap steps
var oStep1 = new sap.ui.commons.RoadMapStep("step1", {label: "Step 1"});
var oStep2 = new sap.ui.commons.RoadMapStep("step2", {label: "Step 2"});
var oStep3 = new sap.ui.commons.RoadMapStep("step3", {label: "Step 3"});
//add steps to the RoadMap
oRMap.addStep(oStep1);
oRMap.addStep(oStep2);
oRMap.addStep(oStep3);
//Set the first step as selected
oRMap.setSelectedStep("step1");
//Set the number of visible steps
oRMap.setNumberOfVisibleSteps(3);
//Place the control on the UI
oRMap.placeAt("sample1");
</script>
This will show three steps in my application. What I want is to add views to each of the steps.
Say I want to add a date picker for first step, table for second step and so on..
How can I do this?
You can achieve this in many ways. I would create a container below your roadmap, where you display different views, one for each step. You could use a NavContainer which handles the navigation
EDIT: it can be as simple as this (I used XMLView notation since I find these a far easier to write, but the same applies for JSViews of course):
<VBox>
<c:RoadMap id="roadMap">
<c:steps>
<c:RoadMapStep id="step1" label="Step 1" />
<c:RoadMapStep id="step2" label="Step 2" />
<c:RoadMapStep id="step3" label="Step 3" />
</c:steps>
</c:RoadMap>
<NavContainer width="100%" height="20rem">
<Page title="Step 1">
<DatePicker />
<Button icon="sap-icon://feeder-arrow" text="Next step" press="doNext" />
</Page>
<Page title="Step 2">
<Text text="Some data"/>
<Button icon="sap-icon://nav-back" text="Previous step" press="doPrevious" />
<Button icon="sap-icon://feeder-arrow" text="Next step" press="doNext" />
</Page>
<Page title="Step 3">
<Text text="Some more data"/>
<Button icon="sap-icon://nav-back" text="Previous step" press="doPrevious" />
<Button icon="sap-icon://feeder-arrow" text="Next step" press="doNext" />
</Page>
</NavContainer>
</VBox>
In the doNext and doPrevious you then increment/decrement the roadmap's selectedStep property with the correct step ID, and you perform a nav.to(target) or nav.back()
See https://sapui5.hana.ondemand.com/sdk/explored.html#/sample/sap.m.sample.NavContainer/preview for info in the NavContainer