WPF userControl wielokrotne użycie

WPF userControl wielokrotne użycie
DZ
  • Rejestracja:prawie 12 lat
  • Ostatnio:ponad 4 lata
  • Postów:115
0

Mam aplikację w której potrzebuję w różnych miejscach komunikować się z wagą poprzez RS232. W tym celu przygotowałem UserControl do komunikacji z wagą, wyświetlania i rejestrowania jej wskazań. Aplikacja jest pisana z myślą o terminalach dotykowych i wykorzystuje okna nakładające się na siebie. Po uruchomieniu aplikacji wybieram użytkownika i loguję się, jak logowanie przebiegnie prawidłowo to wyświetlam następne okno z wyborem trybu pracy, po wyborze trybu pracy przechodzę do kolejnego okna i tutaj pojawia się moja UserControl do komunikacji z wagą. Jej pierwsze uruchomienie odbywa się bezproblemowo -problem pojawia się jeśli wyjdę z tego okna (wykorzystuję najprostszą metodę "Close()" dla okna) i próbuję wejść jeszcze raz w tryb z obsługą wagi - moje UserControl przy ponownej próbie otwarcia zawiesza cały program.

Co do mojego UserControl i jego inicjalizacji:

Kopiuj
    public partial class ucKontrolkaWagi : UserControl
    {
        private Thread waga;

        public ucKontrolkaWagi()
        {
            InitializeComponent();
            
            bt_Tara.IsEnabled = Params.IloscWag > 0;
            bt_Zero.IsEnabled = Params.IloscWag > 0;
            bt_Waga.IsEnabled = Params.IloscWag > 1;
            
            Params.NrAktualnejWagi = 1;

            if (waga != null)
            {
                if (!waga.IsAlive)
                {
                    waga.Start();
                }
            }
            else
            {
                waga = new Thread(odczytWagi);
                waga.Start();
            }
        }

Wywołanie kontroli wagi w XAML okna trybu pracy:

  • definicja:
Kopiuj
xmlns:komponenty="clr-namespace:Terminal.Komponenty"
  • dodanie:
Kopiuj
<komponenty:ucKontrolkaWagi x:Name="KontrolkaWagi" Grid.Column="3" Grid.Row="1" Grid.RowSpan="4" />

Czy na podstawie tego fragmentu kodu ktoś może mi pomóc gdzie tkwi problem? Ta kontrolka ma się pojawić w wielu miejscach (tzn w wielu oknach ale te oknach ale za każdym razem będzie jej pojedyncza instancja) i trochę komplikuje mi to dalsze prace nad oprogramowaniem.

0

Co się dzieje w odczytWagi?

Przy tworzeniu user controlki zawsze waga początkowo będzie równa null i zawsze wpadniesz w tego else. Ważnym pewnie jest jak zwalniasz zasoby przy zamykaniu kontrolki.

abrakadaber
abrakadaber
  • Rejestracja:ponad 12 lat
  • Ostatnio:7 miesięcy
  • Postów:6610
0

po pierwsze port COM można otworzyć tylko raz w całym systemie i dopóki nie zostanie zamknięty nic innego nie może z niego korzystać. Po drugie biorąc to pod uwagę masz dwa wyjścia - albo otwierać port, wysyłać komendę, odbierać odpowiedź i zamykać port za każdym razem albo zrobić obsługę wagi (odczyt i sterowanie) jako singelton (w sensie sposobu działania niekoniecznie implementacji) w aplikacji i podpinać się do niego samym "wyświetlaczem". Które jest lepsze/prostsze? - musisz sam zadecydować.

Ja mam u siebie zrobione tak, że mam obiekt (odpowiada tylko i wyłącznie za komunikację z wagą i odbieranie od niej odpowiedzi) tworzony i konfigurowany przy starcie programu. Udostępnia on metody do sterowania wagą (taruj, zeruj, zważ, ...) oraz zdarzenia zmiana_wagi i ważenie. Pierwsze jest wywoływane za każdym razem jeśli waga się zmieni a drugie po prawidłowym ważeniu (opis dalej). Dzięki temu po pierwsze już podczas uruchamiania programu wiem czy jest komunikacja z modułem wagi czy nie, mogę się podpiąć do wagi w różnych miejscach programu.

Musisz też pamiętać, że jeśli wysyłasz coś do wagi z wątku to musisz używać Invoke jak zmieniasz coś w głównym wątku.

Co do samego ważenia to u mnie działa to tak, że user wysyła żądanie ważenia (po prostu wciska guzik), wątek wysyła żądanie odczytu masy do wagi i dopiero po tym odczycie zwraca zważoną masę. Pierwotna wersja działała tak, że brała ostatnio odczytaną wartość ale czasami była ona nieprawidłowa.

EDIT: co do samego błędu to obstawiam, że nie kończysz wątku po zamknięciu formy i otworzenie innej, a co za tym idzie próba ponownego otwarcia portu wykrzacza wątek


Chcesz pomocy - pokaż kod - abrakadabra źle działa z techniką.
edytowany 1x, ostatnio: abrakadaber
DZ
  • Rejestracja:prawie 12 lat
  • Ostatnio:ponad 4 lata
  • Postów:115
0
TomekWaw napisał(a):

Co się dzieje w odczytWagi?

Kod odczytWagi:

Kopiuj
void odczytWagi()
        {

            SerialPort port = null;

            switch (Params.NrAktualnejWagi)
            {
                case 1:
                    port = Params.Waga1;
                    break;
                case 2:
                    port = Params.Waga2;
                    break;
                case 3:
                    port = Params.Waga3;
                    break;
            }
            
            if (port.IsOpen)
            {
                
                port.DiscardInBuffer();
                port.DiscardOutBuffer();

                while (!Params.czyZatrzymacWage)
                {
                    port.WriteLine("U1DWY");
                    string odczytZPortuWagi = port.ReadLine();
                    char stabilna = odczytZPortuWagi[1]; //zapisanie pierwszego znaku ze stringa jako info o tym czy waga stabilna (S) czy nie (U)
                    Params.stabilizacjaWagi = stabilna;
                    char plusMinus = odczytZPortuWagi[2]; //sprawdzenie drugiego znaku ze stringa czy waga dodatnia (null) czy ujemna (-)
                    Params.plusMinusWagi = plusMinus;
                    if (!tb_wskazaniaWagi.CheckAccess())
                    {
                        Dispatcher.Invoke(DispatcherPriority.Send,
                            (Action)delegate
                            {
                                wyswietlacz(odczytZPortuWagi, stabilna);
                            }
                            );
                    }
                    else
                    {
                        wyswietlacz(odczytZPortuWagi, stabilna);
                    }
                    Thread.Sleep(50);
                }
            }
            else
            {
                if (!tb_wskazaniaWagi.CheckAccess())
                {
                    Dispatcher.Invoke(DispatcherPriority.Send,
                        (Action)delegate
                        {
                            wyswietlacz("Port zamknięty przy starcie", 'S');
                        }
                        );
                }
                else
                {
                    wyswietlacz("Port zamknięty przy starcie", 'S');
                }
                port.Open();
            }
            
        }
TomekWaw napisał(a):

Przy tworzeniu user controlki zawsze waga początkowo będzie równa null i zawsze wpadniesz w tego else. Ważnym pewnie jest jak zwalniasz zasoby przy zamykaniu kontrolki.

Obawiam się że właśnie z tym sobie nie radzę :( Generalnie zamknięcie okna w którym jest kontrolka wagi robię przez najprostsze Close() i komunikacja z wagą jest przerywana (widzę to na indykatorze wagowym) ale pewnie coś gdzieś zostaje. Tutaj mam problem jak prawidłowo zwolnić zasoby i w pełni "odładować" kontrolkę

DZ
  • Rejestracja:prawie 12 lat
  • Ostatnio:ponad 4 lata
  • Postów:115
0
abrakadaber napisał(a):

po pierwsze port COM można otworzyć tylko raz w całym systemie i dopóki nie zostanie zamknięty nic innego nie może z niego korzystać.

Staram się w miejscach gdzie używam portu COM sprawdzać wpierw czy jest już otwarty i nie próbować go otwierać ponownie w przypadku gdy już jest otwarty

abrakadaber napisał(a):

Po drugie biorąc to pod uwagę masz dwa wyjścia - albo otwierać port, wysyłać komendę, odbierać odpowiedź i zamykać port za każdym razem

A masz doświadczenie jaki to ma wpływ na wydajność aplikacji? Generalnie odczyt wagi ma być praktycznie ciągły i nie wiem czy jak będę otwierał port i zamykał za każdym razem to czy to nie wpłynie negatywnie na wydajność.

abrakadaber napisał(a):

albo zrobić obsługę wagi (odczyt i sterowanie) jako singelton (w sensie sposobu działania niekoniecznie implementacji) w aplikacji i podpinać się do niego samym "wyświetlaczem".

To wydaje się sensowniejsze natomiast obawiam się że moja wiedza na temat programowania wciąż jest zbyt mała żeby to tak ładnie ogarnąć.

abrakadaber napisał(a):

Co do samego ważenia to u mnie działa to tak, że user wysyła żądanie ważenia (po prostu wciska guzik), wątek wysyła żądanie odczytu masy do wagi i dopiero po tym odczycie zwraca zważoną masę. Pierwotna wersja działała tak, że brała ostatnio odczytaną wartość ale czasami była ona nieprawidłowa.

U mnie niestety jest odwrotna sytuacja - operator musi cały czas obserwować wyświetlacz wagi i dopiero po osiągnięciu jakiejś określonej wagi zarejestrować odczyt (tzw. standaryzacja). Podczas rejestracji sprawdzam jeszcze czy zapisywany odczyt jest stabilny - to powinno rozwiązać problem o którym piszesz że ostatnia odczytana wartość nie zawsze była prawidłowa.

Z ciekawości - na jakiej wadze pracujesz? Ja podpinam sam indykator wagowy AXIS ME-00 do którego podpinam taką platformę jaka jest potrzebna w danym projekcie.

EDIT: co do samego błędu to obstawiam, że nie kończysz wątku po zamknięciu formy i otworzenie innej, a co za tym idzie próba ponownego otwarcia portu wykrzacza wątek

abrakadaber
abrakadaber
  • Rejestracja:ponad 12 lat
  • Ostatnio:7 miesięcy
  • Postów:6610
0
  1. ja działam z wagami firmy Radwag, Fawag, Mettler Toledo i Rhewa.
  2. co do wydajności przy zamykaniu i otwieraniu połączenia - Radwag w terminalach PUE5 dodał swój serwis, który pośredniczy w komunikacji z wagą. Działa właśnie na takiej zasadzie - przychodzi polecenie, otwarcie portu, wysłanie polecenia do wagi, odpowiedź z wagi, zamknięcie portu, odpowiedź do pytającego. Odpytuję wagę ok 10 razy na sekundę (częściej nie ma sensu - nie widać praktycznie różnicy) i działa to na dziesiątkach terminali. Natomiast na starszych miernikach port mam otwarty od uruchomienia programu aż do jego zamknięcia. Różnicy pomiędzy jednym a drugim nie zauważyłem.
  3. po prostu musisz swoją usercontrol rozbić na dwa niezależne elementy - wątek z obsługą fizyczną miernika i część wizualną. Wątek tworzysz np. przy starcie a zamykasz przy kończeniu

Chcesz pomocy - pokaż kod - abrakadabra źle działa z techniką.
DZ
  • Rejestracja:prawie 12 lat
  • Ostatnio:ponad 4 lata
  • Postów:115
0
abrakadaber napisał(a):

po prostu musisz swoją usercontrol rozbić na dwa niezależne elementy - wątek z obsługą fizyczną miernika i część wizualną. Wątek tworzysz np. przy starcie a zamykasz przy kończeniu

Czyli np podczas uruchamiania programu w tle wywołuję thread który otwiera port, wysyła do miernika zapytanie o odczyt wagi, odczytuje odpowiedź i przechowuje ją np w jakimś ogólnie dostępnym stringu, zamyka port i tak w kółko a w momencie gdy potrzebuję użyć odczytu i zaprezentować go wizualnie to wywołuję userControl który wyświetla aktualną wartość tego stringa do którego mój thread wysyła cały czas w tle odczyty wagi - czy dobrze rozumiem?
Jak rozwiązać kwestie tarowania (lub ustawiania na wadze zera) gdzie muszę wysłać inną komendę zapamiętującą tarę, odczytać jej wartość - muszę przerwać aktualny thread w którym odbywa się odczyt wagi, wysłać komendę tarowania i po jej wykonaniu ponownie wznowić mój thread?

abrakadaber
abrakadaber
  • Rejestracja:ponad 12 lat
  • Ostatnio:7 miesięcy
  • Postów:6610
1
dzilupl napisał(a):

Czyli np podczas uruchamiania programu w tle wywołuję thread który otwiera port, wysyła do miernika zapytanie o odczyt wagi, odczytuje odpowiedź
dokładnie

i przechowuje ją np w jakimś ogólnie dostępnym stringu,

<wg mnie="mnie"> takie podejście jest brzydkie - dużo ładniej jest skorzystać ze zdarzeń (do poczytania http://4programmers.net/C_sharp/Wprowadzenie/Rozdzia%C5%82_6#id-Zdarzenia http://www.altcontroldelete.pl/artykuly/obsluga-zdarzen-w-c-delegaty-i-eventy/ ) </wg mnie>

zamyka port

niekoniecznie - możesz go mieć cały czas otwartego

i tak w kółko

dokładnie

a w momencie gdy potrzebuję użyć odczytu i zaprezentować go wizualnie to wywołuję userControl który wyświetla aktualną wartość tego stringa do którego mój thread wysyła cały czas w tle odczyty wagi - czy dobrze rozumiem?

jeśli użyjesz zdarzeń to wtedy wątek niejako automatycznie będzie wysyłał sygnał o zmianie wagi do kontrolki wyświetlającej (oczywiście trzeba to napisać najpierw)

Jak rozwiązać kwestie tarowania (lub ustawiania na wadze zera) gdzie muszę wysłać inną komendę zapamiętującą tarę, odczytać jej wartość - muszę przerwać aktualny thread w którym odbywa się odczyt wagi, wysłać komendę tarowania i po jej wykonaniu ponownie wznowić mój thread?

ale dlaczego? Możesz dodać sobie zmienną, która będzie "mówiła" wątkowi co ma robić. Np. przed wysłaniem komendy na port możesz zrobić coś takiego

Kopiuj
switch (corobic)
{
  case 0:
    komenda = "odczytaj_wage";
    break;
  case 1:
    komenda = "taruj_wage";
    corobic = 0;
    break;
  case 2:
    komenda = "zeruj_wage";
    corobic = 0;
    break;
}
port_com.write(komenda);

a gdzieś dalej po wciśnięciu taruj corobic = 1


Chcesz pomocy - pokaż kod - abrakadabra źle działa z techniką.
DZ
  • Rejestracja:prawie 12 lat
  • Ostatnio:ponad 4 lata
  • Postów:115
0

abrakadaber, czy możesz mnie jeszcze trochę bardziej "oświecić" :) ?
Zrobiłem obsługę komunikacji z miernikiem jako osobną klasę :

Kopiuj
class IndykatorAXIS_ME00_ObslugaWagi
    {
        public static void standardowaPracaWagi()
        {
            SerialPort port = Params.comWaga1;
            string adresIndykatoraUx = Params.daneWagi1[1].ToString();

            if (!port.IsOpen)
            {
                port.Open();
            }

            while (true)
            {
                switch (Params.trybPracyWagi)
                {
                    case 1: // podstawowy tryb, ciągły odczyt wagi
                        odczytajWage(port, adresIndykatoraUx);
                        break;
                    case 2: // tarowanie wagi
                        Params.trybPracyWagi = 1;
                        break;
                    case 3: // ustawianie zero wagi
                        Params.trybPracyWagi = 1;
                        break;
                }
                Thread.Sleep(Params.threadSleepDlaOdczytuWagi);
            }
        }

        private static void odczytajWage(SerialPort comWagi, string adresUxIndykatora)
        {
            comWagi.DiscardInBuffer();
            comWagi.DiscardOutBuffer();
            comWagi.WriteLine(String.Format("{0}{1}",adresUxIndykatora,IndykatorAXIS_ME00_KomunikatyWazenie.DajAktualnyWynik));
            comWagi.ReadLine();
        }
}

Brak w niej jeszcze obsługi tarowania i zerowania ale to jest kwestia tylko wysłania odpowiednich komunikatów do miernika. Samą komunikację nawiązuję podczas uruchamiania programu i cały czas pracuje ona w tle:

Kopiuj
Thread waga = new Thread(IndykatorAXIS_ME00_ObslugaWagi.standardowaPracaWagi);
waga.Start();

Teraz mam moje UC do prezentacji wskazań wagi - najprostsza wersja z TextBox jako wyświetlaczem i dwoma przyciskami do obsługi tarowania i zerowania. Chcąc wykorzystać zdarzenia to powinienem je stworzyć w obrębie tego UC czy po stronie obsługi samego miernika? W jaki najbardziej odpowiedni sposób powinienem przekazać odczyt do wyświetlacza?

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)