Windows: Set Environment variables for users

For some cases you might need to set environment variables for your .NET application.
This is true to .NET core too, when you are storing your application settings in these variables.

How to set up a script for the job

It’s handy to create a windows command line script for creating these variables, because when you are on a new work environment, than you don’t need to look up the keys and values.

So bring up your most-loved text editor, make your environment_variable_creator.bat, and add the following code:

@echo on
SETX VARIABLE1 VALUE
SETX PREFIX_VARIABLE2 true
pause

If you are using .NET core’s configuration builder with a prefix like this:

				ConfigurationBuilder cfgBuilder = new ConfigurationBuilder();
				cfgBuilder.AddEnvironmentVariables("PREFIX_");
				IConfigurationRoot configuration = cfgBuilder.Build();
				var value = configuration["VARIABLE2"];

Then you need to set your variables with prefix “PREFIX_” in the bat file. In this scenario, VARIABLE1 can not be read, because it does not contain the prefix.

The code sets the variables only for your user account, not for others. You can parameterize the SETX command to set the settings for a different user, but i recommend, to run the script with a command line for the targeted user, because its much easier.

To run your script under other users name, then open your command prompt with ‘Run as… different user’

NuGet lokális Cache törlés egyszerűen

Amennyiben azt feltételezzük, hogy a Visual Studio a nugetek cachelése miatt nem működik úgy, ahogy az elvárt lenne, abban az esetben lehetőségünk van a lokális cache-t üríteni.

GUI megoldás:
ToolsOptionsNuGet Package ManagerPackage manager settings Clear All Nuget cache

CLI megoldás:
1. nuget.exe beszerzése – https://dist.nuget.org/win-x86-commandline/latest/nuget.exe
2. Parancs kiadása

nuget locals all -clear

Xamarin Forms: Custom Rendererek más assemblyből

A Xamarin Forms a CustomRenderer Attribútumot csak a betöltött referenciák között keresi. Ez azt jelenti, hogyha egy másik projektben található a customrenderer, akkor annak a projektnek kifordított dll-jének már a Forms.Init előtt be kell töltődnie. Jó megoldás lehet, ha létrehozunk egy statikus osztályt, aminek a statikus Initalize metódusára ráhívunk Android esetén az onCreate, iOS esetén pedig az AppDelegate FinishedLauching függvényében.

public static class Initainer
{
    public static void Initalize()
    {
        Console.WriteLine("Initainer is initializing Custom renderers")
    }
}

Xamarin Forms: Auto magasságú ListView

Formsban, ha egy ListView HorizontalOptions, VerticalOptions tulajdonságait Fill-re állítjuk, és a ListView egy Grid sorában van mondjuk, amelynek a Height-je Auto, a lista le fogja foglalni a Grid által kínált összes helyet. Ez azért van, mert a ListView measure-nél még nem tudja hogy milyen listaelemek milyen listaelem formátummal fognak megjelenni benne. Így elveszítjük a lehetőséget a dinamikus megjelenítésre, mert a felületünk szét fog csúszni.

Ennek a problémakörnek megoldásaképpen készítettem egy olyan ListView-t, amely képes a listaelemek tényleges lemért magassága után újraméretezni a lista magasságát. Ez a lista, mindig a benne található elemek magassága méretűre fog nyúlni. Meg lehet még fűszerezni annyival, hogy a Separatorok magasságát is beleszámolja, illetve hogy legyen egy maximálisan engedett magassága is, én ezt nem tettem meg, de jó kiindulási alap.

 public class AutoHeightListView : ListView
    {
        public AutoHeightListView()
        {
        }

        protected override void OnPropertyChanged([CallerMemberName] string propertyName = null)
        {
            base.OnPropertyChanged(propertyName);

            // ItemsSource változásakor 
            if (propertyName == nameof(ListView.ItemsSource))
            {
                if(ItemsSource != null)
                {
                    // Kikérjük a ListView feltemapltezett celljeit.
                    IEnumerable<PropertyInfo> pInfos = (this as ItemsView<Cell>).GetType().GetRuntimeProperties();
                    var templatedItems = pInfos.FirstOrDefault(info => info.Name == "TemplatedItems");

                    if (templatedItems != null)
                    {
                        var cells = templatedItems.GetValue(this);
                        if(cells is Xamarin.Forms.ITemplatedItemsList<Xamarin.Forms.Cell> cellsCasted)
                        {
                            if(cellsCasted.Count != 0)
                            {
                                ((ViewCell)cellsCasted.Last()).View.SizeChanged += FirstElementHeightChanged;
                            }
                        }
                    }
                }
            }
        }

        double prevHeight = 0;

        private void FirstElementHeightChanged(object sender, EventArgs e)
        {
            SetHeightRequestByCellsHeight();
        }

        /// <summary>
        /// Megméri minden sor Viewcell magasságát, és az egész listview magasságát ezeknek az összegére állítja be.
        /// </summary>
        private void SetHeightRequestByCellsHeight()
        {
            double calculatedHeight = 0;

            // Kikérjük a ListView feltemapltezett celljeit.
            IEnumerable<PropertyInfo> pInfos = (this as ItemsView<Cell>).GetType().GetRuntimeProperties();
            var templatedItems = pInfos.FirstOrDefault(info => info.Name == "TemplatedItems");

            if (templatedItems != null)
            {
                var cells = templatedItems.GetValue(this);
                foreach (ViewCell cell in cells as Xamarin.Forms.ITemplatedItemsList<Xamarin.Forms.Cell>)
                {
                    calculatedHeight += cell.View.Height;
                }
            }

            if (calculatedHeight == prevHeight)
                return;

            prevHeight = calculatedHeight;
            HeightRequest = calculatedHeight;
        }
    }

Xamarin Forms / iOS: Backspace detektálás Entryben

Androidtól eltérően, iOS-en az EntryRenderer nem rendelkezik overrideolható DispatchKeyEventtel. Viszont magának a UITextFieldnek van egy DeleteBackward metódusa, ami a visszatörlés esetén hívódik meg. Ahhoz, hogy ezt felüldefiniáljuk, örököltetnünk kell egy UITextField-et.

public class UITextFieldWithExtendedFuncionallity : UITextField
{
    public delegate void DeleteBackwardEventHandler(object sender, EventArgs e);

    public event DeleteBackwardEventHandler OnDeleteBackward;


    public void OnDeleteBackwardPressed()
    {
        if (OnDeleteBackward != null)
        {
            OnDeleteBackward(this, new EventArgs());
        }
    }

    public UITextFieldWithExtendedFuncionallity()
    {

    }

    public override void DeleteBackward()
    {
        base.DeleteBackward();
        OnDeleteBackwardPressed();
    }
}

Mivel ez egy natív Custom UserControl, ahelyett, ahogy StackOverFlow-n, és egyéb helyeken ajánlják, nem az EntryRenderer-ben fogjuk beállítani natív kontrolként. StackOverflow-on, és Xamarin Fórumon CTRL+C, CTRL-V kód működik jól, de nem tekinthető szép megoldásnak. Ezeken a helyeken azt ajánlják, hogy az EntryRenderer OnElementChanged metódusában hozzunk létre egy új példányt az általunk örököltetett natív usercontrolból, majd állítsuk be azt natív kontrolként. Ilyenkor létrejön feleslegesen egy UITextField, és mi is létrehozunk egy UITextField gyereket, és az előzőleg létrejött field feleslegesen került a memóriában lefoglalásra, feleslegesen fordított a készülék erőforrásokat a konstruktorok és egyéb inicializáló függvények és procedúrák megfuttatására.

Emiatt, létre kell hoznunk a natív egyedi usercontrolunkhoz egy Renderert a következőképp:

[assembly: ExportRenderer (typeof(Entry), typeof(UITextFieldWithExtendedFuncionallityRenderer))]  
namespace DemoApp.iOS.CustomControlRenderers
{
    public class UITextFieldWithExtendedFuncionallityRenderer : ViewRenderer<Entry, UITextFieldWithExtendedFuncionallity>
    {
        private UITextFieldWithExtendedFuncionallity nativeControl;

        protected override void OnElementChanged(ElementChangedEventArgs<Entry> e)
        {
            base.OnElementChanged(e);
            var model = e.NewElement;

            if (model == null)
            {
                return;
            }

            nativeControl = new UITextFieldWithExtendedFuncionallity();

            SetNativeControl(nativeControl);
        }
    }
}

Majd a Xamarin Forms Custom UserControl rendererjének ősét át kell állítani EntryRendererről az imént létrehozott rendererre.

public class NakedEntryRenderer : UITextFieldWithExtendedFuncionallityRenderer

Az Forms U.C. Elementchangedjében már elérhetőek a fent definiált delegatek.

protected override void OnElementChanged(ElementChangedEventArgs<Entry> e)
        {
            base.OnElementChanged(e);

            if (Control != null)
            {
                if(Control is UITextFieldWithExtendedFuncionallity nativeControl)
                {
                    nativeControl.OnDeleteBackward += NativeControl_OnDeleteBackward;
                }
            }
        }

        private void NativeControl_OnDeleteBackward(object sender, EventArgs e)
        {
            if(Element is CustomEntry formsEntry)
            {
                formsEntry.OnBackspacePressed();
            }
        }

A Forms-os UserControl-ban található BackspacePressed event az Androidos mintára épül.

public class CustomEntry: Entry
{
    public delegate void BackspaceEventHandler(object sender, EventArgs e);
 
    public event BackspaceEventHandler OnBackspace;
 
    public CustomEntry() { }
 
    public void OnBackspacePressed() 
    {
        if (OnBackspace != null)
        {
            OnBackspace(null, null);
        }
    }
}