ProgressBar w ExcelDataReader AsDataSet

ProgressBar w ExcelDataReader AsDataSet
RootX93
  • Rejestracja:prawie 3 lata
  • Ostatnio:ponad rok
  • Postów:19
0

Witam,
Proszę o pomoc w rozwiązaniu problemu.
Wczytuję plik excela, z którego pobieram arkusz "Baza" do datagridview poprzez ExcelDataReader AsDataSet
Po wciśnięciu przycisku wykonuje się zdarzenie poniżej:

Kopiuj
private void btnImportFile_Click(object sender, EventArgs e)
        {
            try
            {
                using (OpenFileDialog ofdImportFile = new OpenFileDialog() { Filter = "Excel file | *.xlsm" })
                {
                    if (ofdImportFile.ShowDialog() == DialogResult.OK)
                    {
                        // wersja stream
                        using (FileStream stream = File.Open(ofdImportFile.FileName, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
                        {

                            using (IExcelDataReader reader = ExcelReaderFactory.CreateOpenXmlReader(stream))
                            {
                                bgWorker.WorkerReportsProgress = true;
                                bgWorker.RunWorkerAsync();

                                dsImport = reader.AsDataSet(new ExcelDataSetConfiguration()
                                {
                                    ConfigureDataTable = (_) => new ExcelDataTableConfiguration()
                                    {
                                        UseHeaderRow = true,
                                        FilterRow = (rowReader) =>
                                        {
                                            var progress = rowReader.Depth / rowReader.RowCount;
                                            return true;
                                            bgWorker.ReportProgress(progress);
                                        }
                                    }
                                });
                                dt = dsImport.Tables["Baza"];

                                //wypełnienie datagridview danymi
                                dgvImportExcel.DataSource = dt;
                                
                            }
                        }
                    }
                }
            }
            catch (Exception ex) { MessageBox.Show(ex.Message); }
           
        }
        
        private void bgWorker_ProgressChanged(object sender, ProgressChangedEventArgs e)
        {
            progressBar.Value = e.ProgressPercentage; 
        }
        
        private void bgWorker_DoWork(object sender, DoWorkEventArgs e)
        {
            for (int i = 0; i <= 100; i++)
            {
                //Thread.Sleep(100);
                bgWorker.ReportProgress(i);
            }
        }

Problem polega na tym, że progressBar pokazuje postęp dopiero po wypełnieniu datagridview i nie wiem dlaczego a oczekiwanym efektem jest pokazanie stanu wczytywania pliku.
Proszę o wskazanie powodu nieprawidłowości i ewentualne rozwiązanie.

katakrowa
  • Rejestracja:około 10 lat
  • Ostatnio:około 2 lata
  • Lokalizacja:Chorzów
  • Postów:1670
0

Nie mam pewności bo w C# ostatnio pisałem 15 lat temu ale możesz zobaczyć czy zadziała taka zmiana?

Kopiuj
 private void bgWorker_ProgressChanged(object sender, ProgressChangedEventArgs e)
        {
            progressBar.Value = e.ProgressPercentage; 
            Application.DoEvents();
        }

Projektowanie i programowanie. Hobbystycznie elektronika i audio oszołom.
edytowany 1x, ostatnio: katakrowa
RootX93
  • Rejestracja:prawie 3 lata
  • Ostatnio:ponad rok
  • Postów:19
0
katakrowa napisał(a):

Nie mam pewności bo w C# ostatnio pisałem 15 lat temu ale możesz zobaczyć czy zadziała taka zmiana?

Kopiuj
 private void bgWorker_ProgressChanged(object sender, ProgressChangedEventArgs e)
        {
            progressBar.Value = e.ProgressPercentage; 
            Application.DoEvents();
        }

Powyższa zmiana nie rozwiązuje problemu :(

_13th_Dragon
  • Rejestracja:ponad 19 lat
  • Ostatnio:2 miesiące
1
Kopiuj
private delegate void CallInvalidate();
private void bgWorker_ProgressChanged(object sender, ProgressChangedEventArgs e)
        {
            progressBar.Value = e.ProgressPercentage; 
            Invoke(new CallInvalidate(progressBar.Invalidate),null);
    }

lub

Kopiuj
private void bgWorker_ProgressChanged(object sender, ProgressChangedEventArgs e)
    {
         progressBar.Value = e.ProgressPercentage; 
         Invoke(new Action(() => progressBar.Invalidate));
    }

Wykonuję programy na zamówienie, pisać na Priv.
Asm/C/C++/Pascal/Delphi/Java/C#/PHP/JS oraz inne języki.
edytowany 3x, ostatnio: _13th_Dragon
RootX93
  • Rejestracja:prawie 3 lata
  • Ostatnio:ponad rok
  • Postów:19
0
_13th_Dragon napisał(a):
Kopiuj
private delegate void CallInvalidate();
private void bgWorker_ProgressChanged(object sender, ProgressChangedEventArgs e)
        {
            progressBar.Value = e.ProgressPercentage; 
            Invoke(new CallInvalidate(progressBar.Invalidate),null);
    }

Dzięki za odpowiedź.
Powyższe rozwiązanie wydłuża czas przy wczytywaniu, natomiast sam wskaźnik postępu wciąż pokazuje się po załadowaniu datagridview i jest jak było.

Druga wersja podobnie.

edytowany 1x, ostatnio: RootX93
_13th_Dragon
  • Rejestracja:ponad 19 lat
  • Ostatnio:2 miesiące
0
RootX93 napisał(a):
_13th_Dragon napisał(a):

Powyższe rozwiązanie wydłuża czas przy wczytywaniu, natomiast sam wskaźnik postępu wciąż pokazuje się po załadowaniu datagridview i jest jak było.

Więc użyj normalnego wątku zamiast BackgroundWorker'a


Wykonuję programy na zamówienie, pisać na Priv.
Asm/C/C++/Pascal/Delphi/Java/C#/PHP/JS oraz inne języki.
RootX93
  • Rejestracja:prawie 3 lata
  • Ostatnio:ponad rok
  • Postów:19
0
_13th_Dragon napisał(a):
RootX93 napisał(a):
_13th_Dragon napisał(a):

Powyższe rozwiązanie wydłuża czas przy wczytywaniu, natomiast sam wskaźnik postępu wciąż pokazuje się po załadowaniu datagridview i jest jak było.

Więc użyj normalnego wątku zamiast BackgroundWorker'a

Nie jestem mistrzem programowania, raczej adeptem, więc nie kojarzę co masz na myśli. Może jakiś przykład.
Poza tym, może problem nie jest w tym wariancie a miejscu wywołania lub parametrze, który jest użyty.
Dlaczego progressBar zaczyna działanie po wypełnieniu grida a nie w czasie ładowania danych z pliku.
Takie tam pytania :)

_13th_Dragon
  • Rejestracja:ponad 19 lat
  • Ostatnio:2 miesiące
0

Wykonuję programy na zamówienie, pisać na Priv.
Asm/C/C++/Pascal/Delphi/Java/C#/PHP/JS oraz inne języki.
RootX93
  • Rejestracja:prawie 3 lata
  • Ostatnio:ponad rok
  • Postów:19
0
WB
  • Rejestracja:prawie 4 lata
  • Ostatnio:10 miesięcy
  • Postów:18
1

Na pewno linijka 27. nie powinna tak wyglądać. Aktualizujesz progres po zwróceniu true?

_13th_Dragon
Heh, nawet nie czytałem, ale kompilator się drze !!!
G1
  • Rejestracja:około 4 lata
  • Ostatnio:3 dni
  • Postów:506
0

Rozwiązanie @_13th_Dragon jest poprawne. Pokaz, jak tworzysz progress i jakie wartości ustawiasz

RootX93
  • Rejestracja:prawie 3 lata
  • Ostatnio:ponad rok
  • Postów:19
0
wielki_bebzon napisał(a):

Na pewno linijka 27. nie powinna tak wyglądać. Aktualizujesz progres po zwróceniu true?

Słuszna uwaga. Spostrzegłem ją.
Dodatkowo rozwiązałem problem choć nie do końca mnie satysfakcjonuje.

Kopiuj
int progress = (int)Math.Ceiling((decimal)rowReader.Depth / (decimal)rowReader.RowCount * (decimal)100);
// progress is in the range 0..100
progressBar.Value = progress;

Zmieniam bezpośrednio wartość Value progressBar
Lecz zauważyłem, że w przypadku gdy w skoroszycie jest więcej niż jeden arkusz to nie zawsze progress dochodzi do 100 na koniec wczytania pliku.
Chyba, że ktoś poda mi jak wczytać w prezentowanym przykładzie, jak wczytać dokładnie ten arkusz, który mnie interesuje bo tego jeszcze nie rozgryzłem :)

RootX93
  • Rejestracja:prawie 3 lata
  • Ostatnio:ponad rok
  • Postów:19
0
gswidwa1 napisał(a):

Rozwiązanie @_13th_Dragon jest poprawne. Pokaz, jak tworzysz progress i jakie wartości ustawiasz

Nie napisałem, że jest błędne tylko, że po wprowadzeniu zmian nic nie zmieniło.
Wszystko co tworzy progress jest w zamieszczonych listingach kropka w kropkę.
Jak napisał wielki_bebzon i co sam zauważyłem wywołanie progressa po zwróceniu true z FilterRow było co najmniej dziwne więc szukałem dlaczego ale nie znalazłem ale wstawienie wywołania progressa przed return true nie wyświetlało danych w gridzie i zwracało komunikat.

_13th_Dragon
  • Rejestracja:ponad 19 lat
  • Ostatnio:2 miesiące
0

Nie przejrzałem się całości, dopiero teraz widzę

Kopiuj
private void btnImportFile_Click(object sender, EventArgs e)
{
     bgWorker.WorkerReportsProgress = true;
     bgWorker.RunWorkerAsync();
}
        
        private void bgWorker_ProgressChanged(object sender, ProgressChangedEventArgs e)
        {
            progressBar.Value = e.ProgressPercentage;
            Invoke(new Action(() => progressBar.Invalidate));
        }
        
        private void bgWorker_DoWork(object sender, DoWorkEventArgs e)
        {
            try
            {
                using (OpenFileDialog ofdImportFile = new OpenFileDialog() { Filter = "Excel file | *.xlsm" })
                {
                    if (ofdImportFile.ShowDialog() == DialogResult.OK)
                    {
                        // wersja stream
                        using (FileStream stream = File.Open(ofdImportFile.FileName, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
                        {

                            using (IExcelDataReader reader = ExcelReaderFactory.CreateOpenXmlReader(stream))
                            {
                                dsImport = reader.AsDataSet(new ExcelDataSetConfiguration()
                                {
                                    ConfigureDataTable = (_) => new ExcelDataTableConfiguration()
                                    {
                                        UseHeaderRow = true,
                                        FilterRow = (rowReader) =>
                                        {
                                            var progress = rowReader.Depth / rowReader.RowCount;
                                            return true;
                                            bgWorker.ReportProgress(progress);
                                        }
                                    }
                                });
                                dt = dsImport.Tables["Baza"];

                                //wypełnienie datagridview danymi
                                dgvImportExcel.DataSource = dt;
                                
                            }
                        }
                    }
                }
            }
            catch (Exception ex) { MessageBox.Show(ex.Message); }
        }

Wydaje mi się że warto zacząć od obejrzenia przykładów użycia backgroundworkera


Wykonuję programy na zamówienie, pisać na Priv.
Asm/C/C++/Pascal/Delphi/Java/C#/PHP/JS oraz inne języki.
RootX93
  • Rejestracja:prawie 3 lata
  • Ostatnio:ponad rok
  • Postów:19
0
_13th_Dragon napisał(a):

Nie przejrzałem się całości, dopiero teraz widzę

Kopiuj
private void btnImportFile_Click(object sender, EventArgs e)
{
     bgWorker.WorkerReportsProgress = true;
     bgWorker.RunWorkerAsync();
}
        
        private void bgWorker_ProgressChanged(object sender, ProgressChangedEventArgs e)
        {
            progressBar.Value = e.ProgressPercentage;
            Invoke(new Action(() => progressBar.Invalidate));
        }
        
        private void bgWorker_DoWork(object sender, DoWorkEventArgs e)
        {
            try
            {
                using (OpenFileDialog ofdImportFile = new OpenFileDialog() { Filter = "Excel file | *.xlsm" })
                {
                    if (ofdImportFile.ShowDialog() == DialogResult.OK)
                    {
                        // wersja stream
                        using (FileStream stream = File.Open(ofdImportFile.FileName, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
                        {

                            using (IExcelDataReader reader = ExcelReaderFactory.CreateOpenXmlReader(stream))
                            {
                                dsImport = reader.AsDataSet(new ExcelDataSetConfiguration()
                                {
                                    ConfigureDataTable = (_) => new ExcelDataTableConfiguration()
                                    {
                                        UseHeaderRow = true,
                                        FilterRow = (rowReader) =>
                                        {
                                            var progress = rowReader.Depth / rowReader.RowCount;
                                            return true;
                                            bgWorker.ReportProgress(progress);
                                        }
                                    }
                                });
                                dt = dsImport.Tables["Baza"];

                                //wypełnienie datagridview danymi
                                dgvImportExcel.DataSource = dt;
                                
                            }
                        }
                    }
                }
            }
            catch (Exception ex) { MessageBox.Show(ex.Message); }
        }

Wydaje mi się że warto zacząć od obejrzenia przykładów użycia backgroundworkera

Przetestowałem przedstawiony przypadek ale zwraca on wyjątek:
"Bieżący wątek musi być ustawiony na tryb jednowątkowego apartamentu, aby można było wykonywać wywołania OLE.
Upewnij się, że w funkcji Main jest zaznaczony element STAThreadAttribute...."

_13th_Dragon
  • Rejestracja:ponad 19 lat
  • Ostatnio:2 miesiące
0

Operacje interfejsowe (między innymi otwarcie dialogu) muszą być w wątku główny, zaś całą praca musi być w wątku.

Kopiuj
        private string openedFileName;
        private object ExcelDataSource;
        private void btnImportFile_Click(object sender, EventArgs e)
        {
                using (OpenFileDialog ofdImportFile = new OpenFileDialog() { Filter = "Excel file | *.xlsm" })
                {
                    if (ofdImportFile.ShowDialog() == DialogResult.OK)
                    {
                        openedFileName=ofdImportFile.FileName;
                        bgWorker.WorkerReportsProgress = true;
                        bgWorker.RunWorkerAsync();
                    }
                }
        }
        
        private void bgWorker_ProgressChanged(object sender, ProgressChangedEventArgs e)
        {
            progressBar.Value = e.ProgressPercentage;
            Invoke(new Action(() => progressBar.Invalidate));
        }
        
        private void bgWorker_DoWork(object sender, DoWorkEventArgs e)
        {
            try
            {
                      // wersja stream
                      using (FileStream stream = File.Open(openedFileName, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
                      {

                          using (IExcelDataReader reader = ExcelReaderFactory.CreateOpenXmlReader(stream))
                          {
                              dsImport = reader.AsDataSet(new ExcelDataSetConfiguration()
                              {
                                  ConfigureDataTable = (_) => new ExcelDataTableConfiguration()
                                  {
                                      UseHeaderRow = true,
                                      FilterRow = (rowReader) =>
                                      {
                                          var progress = rowReader.Depth / rowReader.RowCount;
                                          bgWorker.ReportProgress(progress);
                                          return true;
                                      }
                                  }
                              });
                              ExcelDataSource = dsImport.Tables["Baza"];
                          }
                      }
            }
            catch (Exception ex) { MessageBox.Show(ex.Message); }
        }

        //nie zapomnieć podpiąć pod zdarzenie!
        private void bgWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
          dgvImportExcel.DataSource = ExcelDataSource;
        }

Wykonuję programy na zamówienie, pisać na Priv.
Asm/C/C++/Pascal/Delphi/Java/C#/PHP/JS oraz inne języki.
edytowany 1x, ostatnio: _13th_Dragon
RootX93
  • Rejestracja:prawie 3 lata
  • Ostatnio:ponad rok
  • Postów:19
0
_13th_Dragon napisał(a):

Operacje interfejsowe (między innymi otwarcie dialogu) muszą być w wątku główny, zaś całą praca musi być w wątku.
...

Obecna wersja zgłasza wyjątek po pewnym czasie:
"Nieprawidłowa operacja między wątkami: dla formantu datagridview uzyskiwany jest dostęp z wątku innego niż wątek, w którym został utworzony. "
Na pewno muszę bardziej zgłębić backgroudworker by go zrozumieć bo przykłady, które już poznałem nie dały rozwiązania poza tym, który przedstawiłem bez wykorzystania wspomnianej metody.

edytowany 1x, ostatnio: RootX93
_13th_Dragon
  • Rejestracja:ponad 19 lat
  • Ostatnio:2 miesiące
0

To daj dgvImportExcel.DataSource = ExcelDataSource; w Invoke:
Invoke(new Action(() => dgvImportExcel.DataSource = ExcelDataSource));


Wykonuję programy na zamówienie, pisać na Priv.
Asm/C/C++/Pascal/Delphi/Java/C#/PHP/JS oraz inne języki.
RootX93
  • Rejestracja:prawie 3 lata
  • Ostatnio:ponad rok
  • Postów:19
0
_13th_Dragon napisał(a):

To daj dgvImportExcel.DataSource = ExcelDataSource; w Invoke:
Invoke(new Action(() => dgvImportExcel.DataSource = ExcelDataSource));

Dokonane zmiany sprawiły, że dane wczytują się a w progressBar nic się nie wyświetla.
Ale zmiana zmiennej var progress na int progress już działa:

Kopiuj
int progress = (int)Math.Ceiling((decimal)rowReader.Depth / (decimal)rowReader.RowCount * (decimal)100);
edytowany 1x, ostatnio: RootX93
_13th_Dragon
  • Rejestracja:ponad 19 lat
  • Ostatnio:2 miesiące
0

Postaw breakpoint na 40 wiersz i zobacz ile razy on tam się zatrzyma na jedno odświeżenie.


Wykonuję programy na zamówienie, pisać na Priv.
Asm/C/C++/Pascal/Delphi/Java/C#/PHP/JS oraz inne języki.
RootX93
  • Rejestracja:prawie 3 lata
  • Ostatnio:ponad rok
  • Postów:19
0

Można rzec, że problem został rozwiązany a cel osiągnięty.
Pytanie następne czy można od razu wczytać konkretny arkusz w tym przykładzie, bo gdy skoroszyt zawiera wiele arkuszy to progressBar pokazuje błędy wskazania postępu.
W tym przypadku interesuje mnie tylko arkusz "Baza" a nie wszystkie. Jak zmodyfikować ExcelDataReader do wczytania tylko tego arkusza z uwzględnieniem progresu.

RootX93
  • Rejestracja:prawie 3 lata
  • Ostatnio:ponad rok
  • Postów:19
0
_13th_Dragon napisał(a):

Postaw breakpoint na 40 wiersz i zobacz ile razy on tam się zatrzyma na jedno odświeżenie.

Breakpointa umiem postawić ale jak mam odczytać to o czym piszesz w vs2022 nie wiem.

_13th_Dragon
  • Rejestracja:ponad 19 lat
  • Ostatnio:2 miesiące
0

Wykonuję programy na zamówienie, pisać na Priv.
Asm/C/C++/Pascal/Delphi/Java/C#/PHP/JS oraz inne języki.
RootX93
  • Rejestracja:prawie 3 lata
  • Ostatnio:ponad rok
  • Postów:19
0
_13th_Dragon napisał(a):

https://lmgtfy.app/?q=exceldatareader+read+only+one+sheet

Rozwiązanie jest następujące:

Kopiuj
dsImport = reader.AsDataSet(new ExcelDataSetConfiguration()
                        {
                            FilterSheet = (tableReader, sheetIndex) =>
                            {
                                string sheet = tableReader.Name;
                                if (sheet == "Baza")
                                    return true;
                                else return false;
                            },
                            ConfigureDataTable = (_) => new ExcelDataTableConfiguration()
                            {
                                UseHeaderRow = true,
                                FilterRow = (rowReader) =>
                                {
                                    int progress = (int)Math.Ceiling((decimal)rowReader.Depth / (decimal)rowReader.RowCount * (decimal)100);
                                    bgWorker.ReportProgress(progress);
                                    return true;
                                }
                            }
                        });

Należy skorzystać z właściwości FilterSheet, której sprawdzamy czy dany arkusz jest tym, który nas interesuje i dopiero go wczytujemy, pomijając wszystkie pozostałe.
Tamat zamykam.

_13th_Dragon
  • Rejestracja:ponad 19 lat
  • Ostatnio:2 miesiące
1
Kopiuj
FilterSheet = (tableReader, sheetIndex) => tableReader.Name=="Baza",

Wykonuję programy na zamówienie, pisać na Priv.
Asm/C/C++/Pascal/Delphi/Java/C#/PHP/JS oraz inne języki.
RootX93
  • Rejestracja:prawie 3 lata
  • Ostatnio:ponad rok
  • Postów:19
0
_13th_Dragon napisał(a):
Kopiuj
FilterSheet = (tableReader, sheetIndex) => tableReader.Name=="Baza",

Niech żyje zgrabność :)
Dzięki!!!

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)