[Xamarin] VS2017 cross platform android + web service

[Xamarin] VS2017 cross platform android + web service
XR
  • Rejestracja:prawie 5 lat
  • Ostatnio:7 miesięcy
  • Postów:10
0

Siema.

Uczę się programować na androida. Bazując na tutorialach w VS2017 wybieram nowy Projekt -> Cross Platform (i odznaczam iOS oraz UWP), standard.Udało mi się opanować (po japońsku - w sensie, nie napisami) xamla by stworzyć interfejs.Za pomocą tutków dodałem bibliotekę Zxing i odczytuję kody kreskowe.Stworzyłem sobie prosty web service i postawiłem na IIS.Web service ma dwie metody:1. HelloWorld() - zwraca string "Witaj świecie"2. PobierzDane( string kod kreskowy) - zwraca string[] z danymi (zwykły switch z kilkoma przykładowymi wpisami - na potrzeby nauki)

W celach testowych stworzyłem apkę pod WinForms by przetestować WS (w VS2017 dodaję przez Connected Service połączenie do mojego WS) - wszystko działa mam dwie metody jw. Próbowałem tak samo dodać w apce pod andka (tej cross platform) i tu mam problem bo jak dodam przez Connected Service - to metody jakie mam to HelloWorldAsync() oraz PobierDaneAsync(). Jak chcę wywołać HelloWorldAsync() w funkcji synchronicznej to wali błąd, że nie jest asynchronicznie. Zmienię na async to wali błędem, że funkcja mam mieć jakiś "request", no to piszę HelloWorldAsync("jakiś tekst") - błąd, funkcja nie przyjmuje argumentów.Szukam na google rozwiązania - jest, przepisuję mójWSClient klient = new mójWSClient(EndPointConfiguration.Soap12) i jak na filmiku wywołuję moje label1.Text = HelloWorldAsync(); i dalej błąd "request". Szukam dalej i jest coraz gorzej (dla mnie) - ludzie tworzą jakieś klasy, modele i inne rzeczy by wykonać funkcję z WS - kurczę to nie dla mnie, jestem za zielony w te klocki.

Spróbowałem zrobić tylko pod andka czyli Wybrałem opcję XAMARIN->Android w VS2017. Z WS łączę się przez Connected service (jak w tutku z YT) i jasna ciasna mam obie moje metody, które działają (żadne Async).Oczywiście mogę zrobić pod "czystego" andka ale interfejsu nie mogę zrobić kopiuj-wklej (to największy problem bo pod xaml'em zajęło mi miesiąc zrobienie tego interfejsu - a i tak jest to tylko podstawowy bez żadnych opcji (np.: do Zxing) itp.).

Pytanie jak pod Cross platform połączyć się z WS by to chulało?

Ktos
Moderator
  • Rejestracja:prawie 23 lata
  • Ostatnio:około 13 godzin
1

Zmienię na async to wali błędem, że funkcja mam mieć jakiś "request", no to piszę HelloWorldAsync("jakiś tekst") - błąd, funkcja nie przyjmuje argumentów.Szukam na google rozwiązania - jest, przepisuję mójWSClient klient = new mójWSClient(EndPointConfiguration.Soap12) i jak na filmiku wywołuję moje label1.Text = HelloWorldAsync(); i dalej błąd "request".

Nic nie rozumiem. Pokaż jakieś fragmenty swojego kodu - zwłaszcza tam, gdzie ci mówi, że wywołujesz kod asynchroniczny z synchronicznego.

(ale tak nawiasem to SOAP-owe WebService są coraz rzadziej używane)

XR
  • Rejestracja:prawie 5 lat
  • Ostatnio:7 miesięcy
  • Postów:10
0

Witam.
Załącznik ws.png pokazuje gdzie dodaję połączenie do mojego WebService'u.

Kopiuj
namespace MagLok_XF
{
    
    public partial class MainPage : ContentPage
    {

        public MainPage()
        {
            InitializeComponent();

            Test();
        }
        public async void Test()
        {
            
            StringBuilder sb = new StringBuilder();

            WSTest.MagKodSoapClient klient = new  WSTest.MagKodSoapClient(WSTest.MagKodSoapClient.EndpointConfiguration.MagKodSoap12);
            var hw = await klient.HelloWorldAsync();

            sb.AppendLine(hw.ToString());
            //lb_TowarNazwa.Text = sb.ToString();//stest.Length.ToString();
            await DisplayAlert("Info", sb.ToString(), "OK");
            
        }
    }
}

Kod powyżej to to fragment, w którym chcę przetestować połączenie z WS.
Mój WS ma dwie metody:

  1. HelloWorld() - zwraca string "Witaj swiecie"
  2. Pobierz_Dane_Towaru(string kod_kreskowy) - zwraca string[] - funkcja nie ma połączenia do bazy danych tylko na sztywno kilka rzeczy wpisanych Switch->case
    Przykładowo: Pobierz_Dane_Towaru("20592318") zwraca
    Friki o smaku owocowym
    20591318
    0989999
    2/13C

Jednak gdy kompiluję na komórkę to otrzymuję błąd jak w załączniku blad1.png (na komórce mam tylko biały ekran).
Starałem się robić wg tego filmiku https://www.youtube.com/watch?v=JAmZByNhR8s

Ktos
Moderator
  • Rejestracja:prawie 23 lata
  • Ostatnio:około 13 godzin
1

Przepraszam, nie mogę zreplikować twojego błędu.

Biorę sobie gotowy WCF Soapowy - https://github.com/johanv/WcfSoap, tam jest metoda Hello, która przyjmuje stringa, zwraca stringa. Uruchamiam sobie lokalnie. Tworzę rozwiązanie Xamarin.Forms. W części wspólnej dodaje Service Reference wygenerowane automatycznie (Next, Next, Finish) z linka http://localhost:49294/HelloService.svc?singleWsdl.

Tworzę przycisk. Po jego kliknięciu odpalam kod:

Kopiuj
ServiceReference1.HelloServiceClient hc = new ServiceReference1.HelloServiceClient();
var response = await hc.HelloAsync("Marcin");

Wszystko działa (UWP, akurat nie mam Androida pod ręką do testu na szybko).

Jedyna różnica jaką widzę - używam VS2019.

Jeśli możesz udostępnić jakieś kawałki swojego rozwiązania (może być prywatnie) to możemy spróbować coś zdebugować - ale wydaje mi się, że coś poszło nie tak z generowaniem referencji do usługi.

XR
  • Rejestracja:prawie 5 lat
  • Ostatnio:7 miesięcy
  • Postów:10
0

Przepraszam, że dopiero teraz odpisuję ale w środę był młyn w robocie i nie miałem już ochoty pracować z tekstem.

  1. To kod mojego WS
Kopiuj
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Services;
namespace MagWS
{
    /// <summary>
    /// Opis podsumowujący dla MagKod
    /// </summary>
    [WebService(Namespace = "http://tempuri.org/")]
    [WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
    [System.ComponentModel.ToolboxItem(false)]
    // Aby zezwalać na wywoływanie tej usługi sieci Web ze skryptu za pomocą kodu ASP.NET AJAX, usuń znaczniki komentarza z następującego wiersza. 
    // [System.Web.Script.Services.ScriptService]
    public class MagKod : System.Web.Services.WebService
    {

        [WebMethod]
        public string HelloWorld()
        {
            return "Witaj świecie";
        }

        [WebMethod]
        public string[] Pobierz_Dane_Towaru(string kod_towaru)
        {
            string[] dane = new string[4];  //nazwa, kod_1, kod_2, lokalizacja

            /*dane testowe do odczytu*/
            switch(kod_towaru)
            {
                case "20592318":
                    dane[0] = "Friki o smaku owocowym";
                    dane[1] = "20591318";
                    dane[2] = "0989999";
                    dane[3] = "2/13C";
                    break;
                case "20592301":
                    dane[0] = "Friki o smaku arbuzowym";
                    dane[1] = "85873663";
                    dane[2] = "T1268";
                    dane[3] = "6/413";
                    break;
            }

            return dane;
        }
    }
}

Adres tego to http://90.90.90.102:4440/MagKod.asmx (adres mojego lapka w mojej sieci lokalnej - postawione na IIS na Win7).

  1. Kod z MainPage.xaml.cs
Kopiuj
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Xamarin.Forms;
using ZXing;
using System.Data;
using ZXing.Net.Mobile.Forms;
using System.Windows.Input;
using System.Web;



namespace MagLok_XF
{
    
    public partial class MainPage : ContentPage
    {
        private bool kasuj_tb;
        // int i = 0;
        // string s_kod_odczytany = "";
        //  lcpi.data.oledb.OleDbConnection db_con;
        public DateTime _lastKeyStroke = new DateTime(0);
        public List<char> _barcode = new List<char>(10);
        public int ilosc_kodow_odczytanych = 0;


        public MainPage()
        {
            InitializeComponent();
            /*   lb_OdczytanyKod.Text = "";
               lb_TowarNazwa.Text = "";
               lb_TowarKod1.Text = "";
               lb_TowarKod2.Text = "";
               lb_TowarLokalizacja.Text = "";
               lb_OdczytanaLokalizacja.Text = "";
               lb_LokalizacjaZgodna.Text = "";
               //s_kod_odczytany = "123456";
               */

            Test();
        }

        public async void Scan_Barcode(object sender, EventArgs e)
        {
            var skaner = new ZXingScannerPage();

            await Navigation.PushAsync(skaner);
            skaner.OnScanResult += (result) =>
            {
                Device.BeginInvokeOnMainThread(async () =>
                {
                    await Navigation.PopAsync();
                    lb_OdczytanyKod.Text = result.Text;
                    
                });
            };

            //BarcodeScanView.IsVisible = true;

            //BarcodeScanView.IsScanning = true;
        }

        public void OnToggled(object sender, ToggledEventArgs e)
        {
            if (e.Value == true)
            {
                bt_Czynnosc.Text = "Skanuj kod towaru";
                bt_Czynnosc.IsEnabled = false;
                bt_Czynnosc.IsVisible = true;
                en_Kod.IsEnabled = true;
                en_Kod.IsVisible = true;
                en_Kod.Focus();
            }
            else
            {
                bt_Czynnosc.Text = "Skanuj kod towaru";
                bt_Czynnosc.IsEnabled = true;
                bt_Czynnosc.IsVisible = true;
                en_Kod.IsVisible = false;
                en_Kod.IsEnabled = false;
            }
        }

        private void En_Kod_TextChanged(object sender, TextChangedEventArgs e)
        {
            
            if (kasuj_tb == false)
            {
                kasuj_tb = true;
                int licznik = en_Kod.Text.Split('#').Length - 1;    //liczymy ilość wystąpień #
                //lb_TowarNazwa.Text = licznik.ToString();
                if (licznik == 2)
                {
                    string usunHasze = en_Kod.Text.Replace("#", "");  // usuwamy #
                    lb_OdczytanyKod.Text = usunHasze;
                    ilosc_kodow_odczytanych++;
                    if (ilosc_kodow_odczytanych == 0) bt_Czynnosc.Text = "SKANUJ KOD";
                    else bt_Czynnosc.Text = "SKANUJ LOKALIZACJĘ";
                    Przetwarzaj_Dane(usunHasze, ilosc_kodow_odczytanych);
                    lb_TowarKod2.Text = ilosc_kodow_odczytanych.ToString();
                    en_Kod.Text = "";
                }

                kasuj_tb = false;
            }
        }

        public void Przetwarzaj_Dane(string kod, int il_kod)
        {
            //pobieranie danych z bazy - bla bla bla



            lb_TowarNazwa.Text = "aaaaa";// test.Body.ToString();
            il_kod = 0;
        }

        public void _OnKeyPress(object sender, TextChangedEventArgs e)
        {
            TimeSpan elapsed = (DateTime.Now - _lastKeyStroke);
            if (elapsed.TotalMilliseconds > 100) _barcode.Clear();

            _barcode.Add(Convert.ToChar(e.NewTextValue));
            _lastKeyStroke = DateTime.Now;

            if (e.NewTextValue == "#" && _barcode.Count > 1)
            {
                string msg = new String(_barcode.ToArray());
                lb_OdczytanyKod.Text = msg.Replace("#", "");
                ilosc_kodow_odczytanych++;
                if (ilosc_kodow_odczytanych == 1) lb_OdczytanyKod.Text = "Skanuj lokalizację";
                else lb_OdczytanyKod.Text = "Skanuj kod towaru";
                //przetwarzaj dane
                _barcode.Clear();
            }
        }

        public async void Test()
        {
            
            StringBuilder sb = new StringBuilder();

            WSTest.MagKodSoapClient klient = new WSTest.MagKodSoapClient(WSTest.MagKodSoapClient.EndpointConfiguration.MagKodSoap12);
            
            var hw = klient.HelloWorldAsync();

            
                sb.AppendLine(hw.Result.Body.HelloWorldResult.ToString());
            //sb.AppendLine(hw.ToString());
            //lb_TowarNazwa.Text = sb.ToString();//stest.Length.ToString();
            await DisplayAlert("Info", sb.ToString(), "OK");
            
        }
    }
}

Funkcje co robią:
F1. Scan_Barcode - to funkcja do odczytu przez kamerkę komórki/tabletu (biblioteki ZXing)
F2. OnToggled - do switcha, przełącza mi z ZXinga na ręczny skaner kodów kreskowych
F3. En_Kod_TextChanged - zmiana tekstu w Entry, działa gdy switch jest na On - do odczytu z ręcznego czytnika kodów kreskowych
F4. Przetwarzaj_Dane - tu miała być cała obsługa WebService'u
F5. _OnKeyPress - tu miała być obsługa ręcznego czytnika kodów kreskowych ale android nie potrafi "czytać" z głównego ekranu *jak pod Win Forms)
F6. Test - tu testuję WS
Zawartością Test się nie przejmuj - to wynik kolejnych kombinacji próby rozwiązania problemu.
Załączniki "o1" - "o5" pokazują etapy dodawania WS do projektu, "kompilacja_na_komorke" pokazuje błąd jaki dostaję podczas kompilacji.

Na koniec "wisienka":
załączniki wisienka1 i wisienka2 to test mojego WS zrobiony jako Xamarin Native - po skompilowaniu działa bez pudła.

Pozdrawiam

PS. Edytowałem bo się kod rozjechał - część była jako zwykły tekst a część jako kod (enter przed namespace)

edytowany 1x, ostatnio: XRay
Ktos
Moderator
  • Rejestracja:prawie 23 lata
  • Ostatnio:około 13 godzin
0

Okej, po kolei, bo jest ciekawie. Witamy we wspaniałym świecie Xamarina.

Różnica pomiędzy VS 2017 i VS 2019 tutaj ma znaczenie, chodzi o mechanizm generowania klienta dla usługi WCF/SOAP.

https://forums.xamarin.com/discussion/comment/336701/#Comment_336701
https://github.com/dotnet/wcf/issues/1808

Ale u mnie zadziałało, prawda?

Kiedy wygenerowałem sobie klienta do takiego WebService, jakiego mi tutaj podałeś, to dla UWP wszystko zadziałało. I to w nowoczesny sposób, z async/await. Ale tylko pod UWP - bo pod Androidem nie działało, ale z kompletnie innego powodu niż twój - ogólnie rzecz biorąc w momencie kiedy ja robię HelloWorldAsync to on szuka "HelloWorldAsync" w odpowiedzi SOAP - a tam jest przecież "HelloWorld". I nie znajduje i się wykrzacza z błędem.

No i niestety. Implementacja klienta WCF dla Mono ma buga, i ten błąd nie jest w tym momencie naprawiony - https://github.com/dotnet/wcf/issues/2463.

Można zrobić workaround - trzeba sobie ręcznie wygenerować klienta:

Kopiuj
"C:\Program Files (x86)\Microsoft SDKs\Windows\v10.0A\bin\NETFX 4.8 Tools\SvcUtil.exe" /async http://192.168.8.149:8000/WebService1.asmx /out:Reference.cs

(u mnie akurat svcutil był w tym miejscu)

On wygeneruje tutaj metody asynchroniczne "w starym stylu" - bez async/await. Ale wygeneruje też metody synchroniczne, więc można by ich użyć:

Kopiuj
var address = new System.ServiceModel.EndpointAddress("http://192.168.8.149:8000/WebService1.asmx");
WSTest.MagKodSoapClient klient = new WSTest.MagKodSoapClient(new BasicHttpBinding(), address);

var x = klient.Pobierz_Dane_Towaru("20592318");

I takie coś działa - ale synchronicznie, tj. blokuje wątek UI. Można (a nawet powinno) sobie to opakować w Taski, aby puszczać w tle, albo trzeba używać "starego" systemu, gdzie masz metody w stylu klient.BeginPobierz_Dane_Towaru() i callbacki.

Ale zaznaczę jeszcze raz - jeżeli cokolwiek teraz robisz i robisz to od nowa, to stawianie na SOAP (i ten stary WebService i ASMX i tak dalej) to nie ta droga. Mamy 2020 rok, ASP.NET Core WebAPI, JSON i jedziesz - tak samo wygodne, a na pewno działa ;)

XR
  • Rejestracja:prawie 5 lat
  • Ostatnio:7 miesięcy
  • Postów:10
0

Dzięki za zainteresowanie problemem.

Z tego co piszesz wnioskuję, że lepiej przejść na ASP.NET Core WebAPI (to jest ten REST?) niż banglać się z SOAP.

Znasz może jakiś tutorial dla tego ASP.NET Core WebAPI (taki dla idiotów) i co działa na VS2017 (jak trzeba to przeinstaluję na VS2019) i Win7?

Najśmieszniejsze jest to, że specjalnie wybrałem SOAP i asmx bo jest stary i sprawdzony, i na pewno będzie działać.
Najgorsze, że straciłem kupę czasu na coś co i tak trzeba przekombinować by działało.
W miesiąc zrobiłem (za pomocą tutków neta) interfejs (na relative prawie wszystko) programu na andka (to mój pierwszy program na Androida), dodałem obsługę ZXing'a (za pomocą tutka z neta - podstawowy odczyt kodu bez ustawiania parametrów), ba dodałem nawet możliwość odczytu ze skanera ręcznego (fakt, że przez Entry ale cóż - nie znalazłem innej (łatwej) możliwości, pomijam przechwytywanie USB).

W razie problemów z nowym sposobem będę dalej dręczyć pytaniami.

Pozdrawiam

Ktos
Moderator
  • Rejestracja:prawie 23 lata
  • Ostatnio:około 13 godzin
0

Możesz przejrzeć https://docs.microsoft.com/en-us/aspnet/core/tutorials/first-web-api?view=aspnetcore-3.1&tabs=visual-studio, jest dość ogólne, ale zrozumiesz o co chodzi. Działa na VS2017, tylko musisz doinstalować workload dla ASP.NET Core. Jak potrzebujesz przykładu dla takiego bardzo prostego zabawkowego API to możesz obejrzeć https://github.com/csplb/CatApi.

Jako klient - polecam RestSharp, jest bardzo przyjemny do pracy. Największą wadą jest to, że nie da się łatwo (bez zabawy dodatkowej) wygenerować automatycznie klienta, tak jak to było z "Connected Services" - musisz sobie samemu napisać trochę kodu, ale RestSharp sam zapewni ci wszelkie konwertowanie do i z JSON na twoje obiekty.

Jeżeli chcesz, to mogę ci wysłać materiały, jakie robiłem dla moich studentów (po angielsku) - gdzie właśnie przez RestSharpa budowaliśmy aplikację Xamarin.Forms będącą prostym klientem tego wspomnianego Cat API.

XR
  • Rejestracja:prawie 5 lat
  • Ostatnio:7 miesięcy
  • Postów:10
0

Dzięki za odzew. Przejrzę to co podałeś, jak sobie nie poradzę to poproszę Cię o przesłanie tych materiałów dla studentów.

Kliknij, aby dodać treść...

Pomoc 1.18.8

Typografia

Edytor obsługuje składnie Markdown, w której pojedynczy akcent *kursywa* oraz _kursywa_ to pochylenie. Z kolei podwójny akcent **pogrubienie** oraz __pogrubienie__ to pogrubienie. Dodanie znaczników ~~strike~~ to przekreślenie.

Możesz dodać formatowanie komendami , , oraz .

Ponieważ dekoracja podkreślenia jest przeznaczona na linki, markdown nie zawiera specjalnej składni dla podkreślenia. Dlatego by dodać podkreślenie, użyj <u>underline</u>.

Komendy formatujące reagują na skróty klawiszowe: Ctrl+B, Ctrl+I, Ctrl+U oraz Ctrl+S.

Linki

By dodać link w edytorze użyj komendy lub użyj składni [title](link). URL umieszczony w linku lub nawet URL umieszczony bezpośrednio w tekście będzie aktywny i klikalny.

Jeżeli chcesz, możesz samodzielnie dodać link: <a href="link">title</a>.

Wewnętrzne odnośniki

Możesz umieścić odnośnik do wewnętrznej podstrony, używając następującej składni: [[Delphi/Kompendium]] lub [[Delphi/Kompendium|kliknij, aby przejść do kompendium]]. Odnośniki mogą prowadzić do Forum 4programmers.net lub np. do Kompendium.

Wspomnienia użytkowników

By wspomnieć użytkownika forum, wpisz w formularzu znak @. Zobaczysz okienko samouzupełniające nazwy użytkowników. Samouzupełnienie dobierze odpowiedni format wspomnienia, zależnie od tego czy w nazwie użytkownika znajduje się spacja.

Znaczniki HTML

Dozwolone jest używanie niektórych znaczników HTML: <a>, <b>, <i>, <kbd>, <del>, <strong>, <dfn>, <pre>, <blockquote>, <hr/>, <sub>, <sup> oraz <img/>.

Skróty klawiszowe

Dodaj kombinację klawiszy komendą notacji klawiszy lub skrótem klawiszowym Alt+K.

Reprezentuj kombinacje klawiszowe używając taga <kbd>. Oddziel od siebie klawisze znakiem plus, np <kbd>Alt+Tab</kbd>.

Indeks górny oraz dolny

Przykład: wpisując H<sub>2</sub>O i m<sup>2</sup> otrzymasz: H2O i m2.

Składnia Tex

By precyzyjnie wyrazić działanie matematyczne, użyj składni Tex.

<tex>arcctg(x) = argtan(\frac{1}{x}) = arcsin(\frac{1}{\sqrt{1+x^2}})</tex>

Kod źródłowy

Krótkie fragmenty kodu

Wszelkie jednolinijkowe instrukcje języka programowania powinny być zawarte pomiędzy obróconymi apostrofami: `kod instrukcji` lub ``console.log(`string`);``.

Kod wielolinijkowy

Dodaj fragment kodu komendą . Fragmenty kodu zajmujące całą lub więcej linijek powinny być umieszczone w wielolinijkowym fragmencie kodu. Znaczniki ``` lub ~~~ umożliwiają kolorowanie różnych języków programowania. Możemy nadać nazwę języka programowania używając auto-uzupełnienia, kod został pokolorowany używając konkretnych ustawień kolorowania składni:

```javascript
document.write('Hello World');
```

Możesz zaznaczyć również już wklejony kod w edytorze, i użyć komendy  by zamienić go w kod. Użyj kombinacji Ctrl+`, by dodać fragment kodu bez oznaczników języka.

Tabelki

Dodaj przykładową tabelkę używając komendy . Przykładowa tabelka składa się z dwóch kolumn, nagłówka i jednego wiersza.

Wygeneruj tabelkę na podstawie szablonu. Oddziel komórki separatorem ; lub |, a następnie zaznacz szablonu.

nazwisko;dziedzina;odkrycie
Pitagoras;mathematics;Pythagorean Theorem
Albert Einstein;physics;General Relativity
Marie Curie, Pierre Curie;chemistry;Radium, Polonium

Użyj komendy by zamienić zaznaczony szablon na tabelkę Markdown.

Lista uporządkowana i nieuporządkowana

Możliwe jest tworzenie listy numerowanych oraz wypunktowanych. Wystarczy, że pierwszym znakiem linii będzie * lub - dla listy nieuporządkowanej oraz 1. dla listy uporządkowanej.

Użyj komendy by dodać listę uporządkowaną.

1. Lista numerowana
2. Lista numerowana

Użyj komendy by dodać listę nieuporządkowaną.

* Lista wypunktowana
* Lista wypunktowana
** Lista wypunktowana (drugi poziom)

Składnia Markdown

Edytor obsługuje składnię Markdown, która składa się ze znaków specjalnych. Dostępne komendy, jak formatowanie , dodanie tabelki lub fragmentu kodu są w pewnym sensie świadome otaczającej jej składni, i postarają się unikać uszkodzenia jej.

Dla przykładu, używając tylko dostępnych komend, nie możemy dodać formatowania pogrubienia do kodu wielolinijkowego, albo dodać listy do tabelki - mogłoby to doprowadzić do uszkodzenia składni.

W pewnych odosobnionych przypadkach brak nowej linii przed elementami markdown również mógłby uszkodzić składnie, dlatego edytor dodaje brakujące nowe linie. Dla przykładu, dodanie formatowania pochylenia zaraz po tabelce, mogłoby zostać błędne zinterpretowane, więc edytor doda oddzielającą nową linię pomiędzy tabelką, a pochyleniem.

Skróty klawiszowe

Skróty formatujące, kiedy w edytorze znajduje się pojedynczy kursor, wstawiają sformatowany tekst przykładowy. Jeśli w edytorze znajduje się zaznaczenie (słowo, linijka, paragraf), wtedy zaznaczenie zostaje sformatowane.

  • Ctrl+B - dodaj pogrubienie lub pogrub zaznaczenie
  • Ctrl+I - dodaj pochylenie lub pochyl zaznaczenie
  • Ctrl+U - dodaj podkreślenie lub podkreśl zaznaczenie
  • Ctrl+S - dodaj przekreślenie lub przekreśl zaznaczenie

Notacja Klawiszy

  • Alt+K - dodaj notację klawiszy

Fragment kodu bez oznacznika

  • Alt+C - dodaj pusty fragment kodu

Skróty operujące na kodzie i linijkach:

  • Alt+L - zaznaczenie całej linii
  • Alt+, Alt+ - przeniesienie linijki w której znajduje się kursor w górę/dół.
  • Tab/⌘+] - dodaj wcięcie (wcięcie w prawo)
  • Shit+Tab/⌘+[ - usunięcie wcięcia (wycięcie w lewo)

Dodawanie postów:

  • Ctrl+Enter - dodaj post
  • ⌘+Enter - dodaj post (MacOS)