Custom Maui Handler control creates handler when used in xaml but when created with c# does not create the handler - maui

The repro is a small example based on the maui template.
I created a button called MyButtonView and changed the MainPage to consume that control.
The button is created and shows correctly on the page, but when I try to create just the control as in
var b = new MyButtonView(); the handler is not created and I cant figure out how to get this created.
Notice in the source I have implemented the clicked event to show how the handler is not created. I am sure I am missing something but could someone lead me in the right direction?
Github repro

So it seems that if the control once created has a null handler, you will need to call MyButtonView.ToHandler(mauiContext); sounds simple, but getting the mauiContext is a bit of a pain.
The only way i was able to do this was to do the following in the MauiProgram.cs. This works for windows, have yet to try it with iOS
var builder = MauiApp.CreateBuilder();
builder
.UseMauiApp<App>()
.ConfigureFonts(fonts =>
{
fonts.AddFont("OpenSans-Regular.ttf", "OpenSansRegular");
fonts.AddFont("OpenSans-Semibold.ttf", "OpenSansSemibold");
})
.ConfigureMauiHandlers(handlers =>
{
handlers.AddHandler<DtNavigationView, DtNavigationViewHandler>();
handlers.AddHandler<DtWindowTabView, DtWindowTabViewHandler>();
handlers.AddHandler<DtWindowTabItem, DtWindowTabItemHandler>();
});
builder.UseMauiEmbedding<Application>();
var mauiapp = builder.Build();
mauiContext = new MauiContext(mauiapp.Services);
return mauiapp;
Now you can use the static context to get the object to a handler, by using
MyButtonView.ToHandler(MauiProgram.mauiContext);
Dont think this is the best way to do this but its all i can come up with for now.
Update - this is not the answer to the issue. Storing the MauiContext at this point will result in other issues such as not being able to have the base navigation framework setup.
So the only work around i have found so far that will work for me is to capture the MauiContext was to save it off in the handler when
public override void SetMauiContext(IMauiContext mauiContext) { DtMauiContext.mauiContext = mauiContext; base.SetMauiContext(mauiContext); }
The DtMauiContext is a static i can use it in the view level.
In the Maui source they have Application.Current.FindMauiContext(), it would have been so easy of they just exposed this.

Related

Assign UIElements button.clickable, using Visual Scripting Graph

I am trying to use Unity3D's UIToolkit with Visual Scripting Graph...
Typically, I can Query for the proper element, and then add the necessary function to the callback... However, as I am new to Visual Scripting, I am not sure how to get to this point. I've got the 'clickable' object of the button isolated, but I'm not sure how to assign the follow-up execution in the graph.
Usually in the code, I would do something like
clickable.clicked += ExampleFunction();
What I am messing up in the graph, is how to access the '.clicked' part. I can get the proper button element, and I can isolate it's clickable property, but I can't figure out how to assign some kind of functionality to react to the button getting clicked.
I could use a custom node to make this work, but I am looking for a way to do this with built-in nodes, if possible.
Alright... I had to write a custom node, but I figured this out. Here is the graph for the solution.
You have to grab the UIDocument from whichever GameObject it is attached to... You then need to get the Root Visual Element, do NOT clone or instantiate it. You then need to Query for the desired button, using the name you gave it in the UI Builder. It is easier if you use the U Query Extensions nodes... After that, I just made a custom node to subscribe the functionality. I am not familiar of any nodes that do this.
Here is the 'Subscribe Start Result' node code:
using Unity.VisualScripting;
using UnityEngine;
using UnityEngine.UIElements;
public class SubscribeStartResult : Unit
{
[DoNotSerialize]
[PortLabelHidden]
public ControlInput inputTrigger;
[DoNotSerialize]
[PortLabelHidden]
public ValueInput element;
protected override void Definition()
{
element = ValueInput<Button>("element");
inputTrigger = ControlInput("inputTrigger", (flow) =>
{
flow.GetValue<Button>(element).clicked += () =>
{
Debug.Log("Button clicked");
};
return null;
});
}
}
With this setup, clicking the 'Start Button' in play-mode will log "Button clicked" in the Console.
The 'return null;' line is an artifact of the lambda. It is required to continue the control flow in the event this node has a follow-up... Otherwise, this combination of nodes and code allow you to assign callbacks for the UI Builder elements, using the Visual Scripting Graph.

Need to show a card widget and after some delay automatically show another card widget regarding google workspace add-on creation

I need to show homeCard() and after I need to show settingsCard() automatically. Since I coudn't find a right method in app-script documentation I need some help for do this task.
Here I provided the code
function nevigateToUserSelectionPage(e) {
var navigation = CardService.newNavigation();
var builder = CardService.newActionResponseBuilder();
var userSelectionCardNavigation = navigation.pushCard(settingsCard());
return builder.setNavigation(userSelectionCardNavigation).build();
}
function homeCard() {
builder = CardService.newCardBuilder();
section = CardService.newCardSection();
let participantsText = CardService.newTextParagraph()
.setText("<u>Home card here</u>");
let blink = CardService
.newImage()
.setImageUrl('https://res.cloudinary.com/deez2bddk/image/upload/v1646709349/icons8-dots-loading_x9q7jv.gif');
section.addWidget(blink);
section.addWidget(participantsText);
section.addWidget(AddSplah);
builder.addSection(section);
console.log('home card triggered!!!');
return builder.build();
}
function settingsCard() {
//const myTimeout = setTimeout(5000);
Utilities.sleep(10000);
builder = CardService.newCardBuilder();
section = CardService.newCardSection();
console.log('Settings card triggered!!!');
let participantsText = CardService.newTextParagraph()
.setText("<u>This is Settings Page....</u>");
section.addWidget(participantsText);
section.addWidget(getAuthenticationStepperImage());
builder.addSection(section);
return builder.build();
}
in code.gs file
function mainController() {
return homeCard();
}
Above code blocks I need to execute homeCard() function and then , settingsCard() but I can`t find a proper solution in workspace add-on creation documentation provided by google.
After doing some research, I think the CardService does not provide a method for non-interactive updates.
You can update the view based on user click interaction, as you can see in the Cats Quickstart. When the user clicks the cat image changes the image updates due the URL has a new parameter via new Date().getTime().
Apart from this, you have the triggers provided by Google, such as: homepageTrigger for common use case or onItemsSelectedTrigger specifically for Drive. You can review the full list here.
In summary: I think that what are you trying to achieve actually is not currently feasible within CardService.
If you wish Google adds some kind of time driven trigger to Google Workspace Add-ons, request it via this form.
Remember that in the actual state, HTML/CSS is not allowed, maybe this would be another possible path for your Feature Request.

Vaadin Flow: How to tell if a Component is attached

How does one reliably find out if a given Component is actually present in the DOM?
Until now I used Component.getUI().isPresent() which is supposed to able to determine if the Component is attached to a UI.
It also might be that I ran into an edge case as the Components in question are encapsulated in a ComponentRenderer which is managed by a Grid.
I need to access these Componets via JavaScript like this:
void setValue(Component comp, Value value){
Runnable callJs = comp.getUI().ifPresent(ui -> ui.getPage().executeJs("someCall($0)", value));
if(comp.isAttached()){
// execute it right away
callJs.run();
} else {
// execute onAttach
comp.addAttachListener(evt -> callJs.run());
}
}
After some digging I stumbled across the StateNode API:
comp.getElement().getNode().isAttached()

Disable logging on FileConfigurationSourceChanged - LogEnabledFilter

I want Administrators to enable/disable logging at runtime by changing the enabled property of the LogEnabledFilter in the config.
There are several threads on SO that explain workarounds, but I want it this way.
I tried to change the Logging Enabled Filter like this:
private static void FileConfigurationSourceChanged(object sender, ConfigurationSourceChangedEventArgs e)
{
var fcs = sender as FileConfigurationSource;
System.Diagnostics.Debug.WriteLine("----------- FileConfigurationSourceChanged called --------");
LoggingSettings currentLogSettings = e.ConfigurationSource.GetSection("loggingConfiguration") as LoggingSettings;
var fdtl = currentLogSettings.TraceListeners.Where(tld => tld is FormattedDatabaseTraceListenerData).FirstOrDefault();
var currentLogFileFilter = currentLogSettings.LogFilters.Where(lfd => { return lfd.Name == "Logging Enabled Filter"; }).FirstOrDefault();
var filterNewValue = (bool)currentLogFileFilter.ElementInformation.Properties["enabled"].Value;
var runtimeFilter = Logger.Writer.GetFilter<LogEnabledFilter>("Logging Enabled Filter");
runtimeFilter.Enabled = filterNewValue;
var test = Logger.Writer.IsLoggingEnabled();
}
But test reveals always the initially loaded config value, it does not change.
I thought, that when changing the value in the config the changes will be propagated automatically to the runtime configuration. But this isn't the case!
Setting it programmatically as shown in the code above, doesn't work either.
It's time to rebuild Enterprise Library or shut it down.
You are right that the code you posted does not work. That code is using a config file (FileConfigurationSource) as the method to configure Enterprise Library.
Let's dig a bit deeper and see if programmatic configuration will work.
We will use the Fluent API since it is the preferred method for programmatic configuration:
var builder = new ConfigurationSourceBuilder();
builder.ConfigureLogging()
.WithOptions
.DoNotRevertImpersonation()
.FilterEnableOrDisable("EnableOrDisable").Enable()
.LogToCategoryNamed("General")
.WithOptions.SetAsDefaultCategory()
.SendTo.FlatFile("FlatFile")
.ToFile(#"fluent.log");
var configSource = new DictionaryConfigurationSource();
builder.UpdateConfigurationWithReplace(configSource);
var defaultWriter = new LogWriterFactory(configSource).Create();
defaultWriter.Write("Test1", "General");
var filter = defaultWriter.GetFilter<LogEnabledFilter>();
filter.Enabled = false;
defaultWriter.Write("Test2", "General");
If you try this code the filter will not be updated -- so another failure.
Let's try to use the "old school" programmatic configuration by using the classes directly:
var flatFileTraceListener = new FlatFileTraceListener(
#"program.log",
"----------------------------------------",
"----------------------------------------"
);
LogEnabledFilter enabledFilter = new LogEnabledFilter("Logging Enabled Filter", true);
// Build Configuration
var config = new LoggingConfiguration();
config.AddLogSource("General", SourceLevels.All, true)
.AddTraceListener(flatFileTraceListener);
config.Filters.Add(enabledFilter);
LogWriter defaultWriter = new LogWriter(config);
defaultWriter.Write("Test1", "General");
var filter = defaultWriter.GetFilter<LogEnabledFilter>();
filter.Enabled = false;
defaultWriter.Write("Test2", "General");
Success! The second ("Test2") message was not logged.
So, what is going on here? If we instantiate the filter ourselves and add it to the configuration it works but when relying on the Enterprise Library configuration the filter value is not updated.
This leads to a hypothesis: when using Enterprise Library configuration new filter instances are being returned each time which is why changing the value has no effect on the internal instance being used by Enterprise Library.
If we dig into the Enterprise Library code we (eventually) hit on LoggingSettings class and the BuildLogWriter method. This is used to create the LogWriter. Here's where the filters are created:
var filters = this.LogFilters.Select(tfd => tfd.BuildFilter());
So this line is using the configured LogFilterData and calling the BuildFilter method to instantiate the applicable filter. In this case the BuildFilter method of the configuration class LogEnabledFilterData BuildFilter method returns an instance of the LogEnabledFilter:
return new LogEnabledFilter(this.Name, this.Enabled);
The issue with this code is that this.LogFilters.Select returns a lazy evaluated enumeration that creates LogFilters and this enumeration is passed into the LogWriter to be used for all filter manipulation. Every time the filters are referenced the enumeration is evaluated and a new Filter instance is created! This confirms the original hypothesis.
To make it explicit: every time LogWriter.Write() is called a new LogEnabledFilter is created based on the original configuration. When the filters are queried by calling GetFilter() a new LogEnabledFilter is created based on the original configuration. Any changes to the object returned by GetFilter() have no affect on the internal configuration since it's a new object instance and, anyway, internally Enterprise Library will create another new instance on the next Write() call anyway.
Firstly, this is just plain wrong but it is also inefficient to create new objects on every call to Write() which could be invoked many times..
An easy fix for this issue is to evaluate the LogFilters enumeration by calling ToList():
var filters = this.LogFilters.Select(tfd => tfd.BuildFilter()).ToList();
This evaluates the enumeration only once ensuring that only one filter instance is created. Then the GetFilter() and update filter value approach posted in the question will work.
Update:
Randy Levy provided a fix in his answer above.
Implement the fix and recompile the enterprise library.
Here is the answer from Randy Levy:
Yes, you can disable logging by setting the LogEnabledFiter. The main
way to do this would be to manually edit the configuration file --
this is the main intention of that functionality (developers guide
references administrators tweaking this setting). Other similar
approaches to setting the filter are to programmatically modify the
original file-based configuration (which is essentially a
reconfiguration of the block), or reconfigure the block
programmatically (e.g. using the fluent interface). None of the
programmatic approaches are what I would call simple – Randy Levy 39
mins ago
If you try to get the filter and disable it I don't think it has any
affect without a reconfiguration. So the following code still ends up
logging: var enabledFilter = logWriter.GetFilter();
enabledFilter.Enabled = false; logWriter.Write("TEST"); One non-EntLib
approach would just to manage the enable/disable yourself with a bool
property and a helper class. But I think the priority approach is a
pretty straight forward alternative.
Conclusion:
In your custom Logger class implement a IsLoggenabled property and change/check this one at runtime.
This won't work:
var runtimeFilter = Logger.Writer.GetFilter<LogEnabledFilter>("Logging Enabled Filter");
runtimeFilter.Enabled = false/true;

Unity Entity Framework within ASP.NET WebAPI 2

I have a very weird problem with Unity here. I have the following:
public class UnityConfig
{
public static void RegisterTypes(IUnityContainer container)
container.RegisterType<IDBContext, MyDbContext>(new PerThreadLifetimeManager());
container.RegisterType<IUserDbContext>(new PerThreadLifetimeManager(), new InjectionFactory(c =>
{
var tenantConnectionString = c.Resolve<ITenantConnectionResolver>().ResolveConnectionString();
return new UserDbContext(tenantConnectionString);
}));
}
}
and then in the WebApiConfig.cs file within the Reigster method:
var container = new UnityContainer();
UnityConfig.RegisterTypes(container);
config.DependencyResolver = new UnityResolver(container);
Basically, what I want to happen in the above code is on every request to the API, I want Unity to new up a UserDbContext based on the user (multi-tenant kind of environment). Now the TenantConnectionResolver is responsible for figuring out the Connection String and then I use that connection string to new up UserDbContext.
Also note (not shown above) that TenantConnectionResolver takes an IDbConext in its constructor because I need it to figure out the connection string based on user information in that database.
But for some reason, the code within the InjectionFactory runs at random times. For example, I call //mysite.com/controller/action/1 repetitively from a browser, the code in the InjectionFactory will occasionally run but not on each request.
Am I incorrectly configuring Unity? Has anybody encountered anything similar to this?
Thanks in advance
The problem is very likely related to the LifetimeManager you are using. PerThreadLifetimeManager is not adapted in a web context, as threads are pooled and will serve multiple requests in sequence.
PerRequestLifetimeManager is probably what you want to use.