Visual Studio designer, własna kontrolka (WebControl) i jej reprezentacja w DesignMode
Strona główna--artykuly--VisualStudioDesigner
  

Visual Studio designer, własna kontrolka (WebControl) i jej reprezentacja w DesignMode

  1. Filozofia designera
  2. Rysowanie kontrolki w runtime.
  3. Ukrywanie wybranych właściwości kontrolki w property window.
  4. Dodanie do property window nie istniejących w kontrolce właściwości.
  5. Dodanie do property window nie istniejącego w kontrolce zdarzenia (event).
  6. Własne zdarzenia w designerze oraz wykorzystanie serwisów.
  7. Generacja własnej procedury obsługi w pliku aspx.
  8. Załączniki

1.Filozofia designera

Jeśli w środowisku programistycznym .NET VisualStudio zdażyło się wam kiedyś projektować własne webowe kontrolki być może zauważyliście że w designerze jaki jest wbudowany w VisualStudio te własne kontrolki w czasie projektowania nie zachowują się tak jak powinny czyli zgodnie z logiką działania w runtime.

Designer jest ciekawie pomyślanym udogodnieniem ale rządzą nim trochę inne reguły niż standardową przeglądarką internetową dodatkowo nie jest w pełni zgodny z działaniem przeglądarki IE oraz nie działają w nim kody javascript co powoduje że dynamiczne zachowanie kontrolki w designerze może odbiegać od starannie zaprojektowanej logiki działania którą można obserwować w aplikacji.

Zwykle nie stanowi to strasznie wielkiego problemu w sytuacji gdy aplikacja jest niewielka i działanie kontrolek jest dobrze znane autorowi który ich używa w formatkach. Sytuacja jednak się komplikuje przy dużych systemach gdzie kontrolki są używane wielokrotnie przez programistów którzy tylko projektują formatki a nie pisali logiki działania kontrolek.

Było by dobrze żeby kontrolka w trakcie projektowania swoim zachowaniem i wyglądem naśladowała zachowanie z "runtime". Można oczywiście w funkcji generującej obraz html kontrolki (RenderContents lub Render) zrobić tak:

if (this.DesignMode)
{
	//tworzenie kodu html dla trybu design
}
else
{
	//tworzenie kodu html dla trybu runtime
}

I też się da zadbać o różny wygląd kontrolki w czasie działania programu i w czasie projektowania formatki. Jednak jest lepsze rozwiązanie umożliwiające zarówno rozdzielenie kodów dla tych dwóch trybów działania ale również dzięki temu będziemy mieli większe (znacznie) możliwości wpłynięcia zarówno na wygląd kontrolki w trybie design jak i na jej dynamiczne zachowanie.

Wystarczy przed kodem klasy naszej kontrolki dodać atrybut "Designer" definiiujący która klasa będzie obsługiwała tryb design tej kontrolki, może to np. wyglądać tak.

	[Designer(typeof(myControlDesigner))]
	public class MyControl : WebControl
	{
	}

Jak widać nasza kontrolka o nazwie MyControl dziedziczy sobie po klasie "WebControl" natomiast atrybut "Designer" określa że zachowaniem kontrolki w trybie projektowania będzie zarządzała klasa "myControlDesigner" która zaczyna się tak:

	public class myControlDesigner : ControlDesigner
	{
	}

Jak widać nasza klasa Designera w tym wypadku dziedziczy po klasie "ControlDesigner" która to klasa jest standardowa klasą wywoływaną przez VisualStudio do obsługi prostych jednoelementowych kontrolek pochodzących od wspólnej dla nich klasy "System.Web.UI.Control". Jeśli sami nie zdefiniujemy naszej własnej klasy obsługującej tryb "Design" to ta właśnie klasa jest używana do stworzenia obrazu kontrolki gdy przełączamy w VS widok z kodu na tryb "Design"

W tym tutorialu będę opisywał modyfikację trybu design dla kontrolek Web ale w dosyć podobny sposób robi się to dla kontrolek WinFormsowych.

Jak już napisałem jeśli sami w atrybucie "Design" nie zdefiniujemy tego której klasy Designer ma użyć do zbudowania widoku kontrolki w trybie "Design" to on sam wtedy do obsługi kontrolki wybierze klasę najbardziej mu pasującą i tak np. jeśli będzie miał kontrolkę typu System.Web.UI.WebControls.Panel (otaczającą inne kontrolki) to najwłaściwszą klasą do obsługi trybu design dla tej kontrolki będzie klasa System.Web.UI.Design.WebControls.PanelContainerDesigner, a jeśli będzie to kontrolka użytkownika UserControl (składającą się z kilku innych kontrolek) najwłaściwszą klasą trybu design będzie w tym przypadku klasa System.Web.UI.Design.UserControlDesigner, projektowana do obsługi kontrolek złożonych zawierających w sobie inne kontrolki itd. itd..

Analogicznie jeśli sami projektujemy własną kontrolkę musimy się chwilę zastanowić która z klas designera (z przestrzeni nazw System.Web.UI.Design.*) najlepiej opisuje naszą kontrolkę i po tej klasie właśnie zdzidziczyć zachowanie w naszej klasie designera.

2.Rysowanie kontrolki w runtime.

Mająć już przygotowaną kontrolkę działającą w trybie "runtime" z określoną logiką i wyglądem chcielibyśmy aby działała ona i wyglądała przynajmniej podobnie w trybie design. Tak jak kontrolka dla trybu "runtime" ma przykrytą metodę "RenderContents" (lub "Render" jeśli nie chcemy mieć kontrolki otoczonej elementem span) która to metoda decyduje o tym jak ma ta kontrolka wyglądać w "runtime" tak samo istnieje analogiczna metoda dla naszej klasy designera którą należy przykryć aby móc przedefiniować wygląd i zachowanie kontrolki i "DesignMode". Tą metodą jest metoda "GetDesignTimeHtml". Metoda ta ma po prostu zwrócić stringa zawierającego cały kod html tworzący kontrolkę.

Oczywiście bardzo często może się okazać że zarówno w "runtime" jak i w "DesignMode" kod html będzie dosyć podobny a będzie tylko różnił się pewnymi szczegółami specyficznymi akurat dla przeglądarki lub dla designera wbudowanego w VisualStudio. Dość przyjemną właściwością designera z VS jest to że widzi wszystkie zdefiniowane w projekcie pliki szablonów wyglądu (css) co bardzo ułatwia zachowanie jednolitego wyglądu pomiędzy środowiskiem pracy a projektowania. Niestety ponieważ w designerze nie działają skrypty języka javascript dlatego jeśli jakaś część zachowania kontrolek zależy od dynamicznych kodów wykonywanych po stronie przeglądarki to zachowanie podobnego działania po stronie "designera" może wymagać utrzymywanie specjalnych szablonów dla trybu projektowania (co odradzam) albo można w tym celu wykorzystywać atrybut style lub class.

Wszystko właściwie zależy tylko od tego w jaki sposób jest zbudowana kontrolka i może się nawet okazać że wystarczy po prostu pobrać obraz html controlki z runtime jak i może się okazać w przypadku wielu operacji wykonywanych dynamicznie przez Javascript (podmienianie styli i klas) że zasymulowanie podobności wyglądu i działania będzie wymagało sporo pracy.

Przykładowo poniżej widzimy kod tworzący jakąś prostą kontrolkę

		protected override void RenderContents(HtmlTextWriter writer)
		{
			writer.AddAttribute(HtmlTextWriterAttribute.Id, this.ID);
			writer.AddAttribute(HtmlTextWriterAttribute.Value, this.Text);
			if(string.IsNullOrEmpty(this.Text))
				writer.AddAttribute(HtmlTextWriterAttribute.Class, "empty");
			else
				writer.AddAttribute(HtmlTextWriterAttribute.Class, "notempty");
			writer.AddAttribute(HtmlTextWriterAttribute.Onchange, string.Format("onChange({0});", this.ClientID));
			writer.RenderBeginTag(HtmlTextWriterTag.Input);
			writer.RenderEndTag();
		}

Jak widać kod ten jest prosty i jedynym poleceniem sterowania przepływu jest "if" sprawdzający stan właściwośći "Text" i w zależności od jego wypełnienia ustawia właściwą klasę "css" do zarządzania kolorem kontrolki.

I wszystko jest ok i w designerze spokojnie mogli byśmy wykorzystać html pobierany z kontrolki z jej metody "Render". Ale teraz wyobraźmy sobie to że nasza aplikacja przykładowo nie jest aplikacją wykorzystującą PostBacki a jest aplikacją WEB 2.0 korzystającą z AJAXa i w takim przypadku metoda do renderowania kontrolki tylko raz zostanie wykonana przy starcie strony a wszelkie dalsze zmiany w wyglądzie i zachowaniu strony zapewnią metody dynamicznego JavaScriptu po stronie przeglądarki.

W naszym prościutkim przykładzie będzie to np. dodanie do aplikacji następującego przykładowego kodu w javascript.

var onChange = function (par1) 
{
    if (par1.value.length <= 0)
        par1.setAttribute("class", "empty");
    else
        par1.setAttribute("class", "notempty");
}

Czyli po stronie przeglądarki będziemy dynamicznie sprawdzać czy po zmianie wartości w kontrolce mają się zmienić klasy opisujące jej wygląd.

Jak od razu widać właściwie stosowanie "IFa" z metody "RenderContents" przestaje mieć sens ponieważ bez "PostBacków" prezentowana funkcjonalność będzie zawsze wywołana tylko raz i nie będzie w tym żadnego dynamizmu i interakcji z użytkownikiem.

Dynamizm zapewnia JavaScript który w przypadku wbudowanego w Visual Studio designera nie działa (może kiedyś będzie działał a może i nie).

W takim wypadku aby zepewnić pewną symulację dynamicznego działania wpływu zmiany właściwości "Text" na wygląd tego co widzimy w designerze, Designerowa metoda opisująca generację kodu html kontrolki mogła by np. wyglądać tak:

		public override string GetDesignTimeHtml()
		{
			StringWriter sw = new StringWriter();
			HtmlTextWriter writer = new HtmlTextWriter(sw);
			writer.AddAttribute(HtmlTextWriterAttribute.Id, ((MyControl)this.Component).ID);
			writer.AddAttribute(HtmlTextWriterAttribute.Value, ((MyControl)this.Component).Text);
			if (string.IsNullOrEmpty(((MyControl)this.Component).Text))
			{
				//	writer.AddStyleAttribute(HtmlTextWriterStyle.BackgroundColor, "#66FF66");
				writer.AddAttribute(HtmlTextWriterAttribute.Class, "empty");
			}
			else
			{
				writer.AddAttribute(HtmlTextWriterAttribute.Class, "notempty");
				//	writer.AddStyleAttribute(HtmlTextWriterStyle.BackgroundColor, "#FFFF66");
			}
			writer.RenderBeginTag(HtmlTextWriterTag.Input);
			writer.RenderEndTag();

			return sw.ToString();
		}
Design mode tryb (w tym wypadku kolor)kontrolki zależy od wypełnienia property text

I już, po każdej zmianie wartości propertisa kontrolki silnik designera wywołuje virtualną metodę (tak tak można ją przedefiniować) "OnComponentChanged" która to z koleji gdzieś w swoim ciele woła metodę "UpdateDesignTimeHtml" która wołając metodę "GetDesignTimeHtml" (tą której domyślne działanie właśnie przykryliśmy) powoduje ponowne przerysowanie wyglądu twojej kontrolki czyli po prostu jej kod html zostanie stworzony od nowa i od nowa zostenie zaktualizowany odpowiedni "Tag" opisujący twoją kontrolkę a więc i jej obraz wizualny zostanie zmieniony (UFF). Krótko mówiąc po każdej zmienie jakiejś właściwości kod html opisujący kontrolkę jest generowany od nowa i obraz kontrolki w designerze jest odświeżany.

widok kodu aspx kontrolki zależy od wypełnienia property text

Moglibyśmy również uzyskać podobny efekt zostawiając w spokoju metodę "GetDesignTimeHtml" a można po prostu przejąć metodę designera "OnComponentChanged" która to jest zawsze wołana w momęcie zmiany wartości jakiegokolwiek property i w niej w zależności od odczytanej wartości property "Text" odpowiednio modyfikować właściwość designera o nazwie "Tag" (odzidziczyliśmy ją razem z klasą "ControlDesigner") a która to właściwość odzwierciedla nam programistyczny obraz tego co mamy w pliku aspx. Przykładowy kod takie działanie realizujący mógłby wyglądać np. tak:

				public override void OnComponentChanged(object sender, ComponentChangedEventArgs ce)
				{
					if(ce.Member.Name == "Text")
					{
						if(string.IsNullOrEmpty(ce.NewValue))
							Tag.SetAttribute("CssClass", "empty");
						else
							Tag.SetAttribute("CssClass", "notempty");
							
						this.UpdateDesignTimeHtml();
					}
					else
					{
						base.OnComponentChanged(sender, ce);
					}
				}
W razie takiego podejścia pamiętajmy jednak że właściwość "Tag" reprezentuje nam kontekst kontrolki czyli właściwości odziedziczone z kontrolki "WebControl" (albo mówiąc inaczej to co możemy w tagu kontrolki ustawić w pliku aspx) a nie obraz HTML kontrolki wysyłany do przeglądarki.

Warte odnotowania jest również to że jeśli właśnie zmienianym property jest to którego działanie chcemy przedefiniować to nie możemy pozwolić aby wywołana została bazowa metoda "OnComponentChanged" ponieważ przywróći wtedy domyślną (poprzednią) wartość obrazu Taga w pliku aspx. Zamiast tego musimy tylko wywołać metodę UpdateDesignTimeHtml aby uaktualnić kod HTML w designerze. Jednak dla pozostałych property które zmieniamy musimy wywołać podstawową metodę z klasy bazowaj ponieważ jeśli tego nie zrobimy to wartości propertisów które zmieniamy nie będą przepisywane do pliku aspx (czasem właśnie o to nam może chodzić). Przedstawiona powyżej metoda modelowania zachowania kontrolki wydaje się nawet bardziej dynamiczna i ok niż metoda odrysowywania za każdym razem kontrolko od nowa, można w skrócie powiedzieć że metoda odrysowująca przypomina działanie bliskie "PostBack"om a metoda "eventowa" bardziej przypomina logikę dynamicznego HTMLa co kto woli i czego potrzebuje, można również dowolnie mieszać obie te metody i myślę do dopuki się nie pogubimy jest ok. Powyższa metoda jest również pomocna jeśli zależy nam po prostu na utrzymywaniiu pliku aspx w odpowiednim porządku tak aby niestandardowe ustawienia kontrolki miały swoje odzwierciedlenie w pliku aspx.

3.Ukrywanie wybranych właściwości kontrolki w property window

Standardowo wszystkie publiczne właściwości kontrolek i ich zdażenia (eventy) są wyświetlane w oknie właściwości (property window). Oczywiście nie zawsze jest to nam na rękę ponieważ może chcielibyśmy niektórych nie pokazywać w tym oknie pomimo że są one dostępne (publiczne) z poziomu kodu. Okazuje się że można zarządać tym zachowaniem designera na dwa sposoby. Prostszy z nich polega na dodaniu do klasy kontrolki atrybutu "Browsable(false)" i od tej chwili property występujący po tej deklaracji nie będzie już więcej widoczny w oknie właściwości. Metoda prosta i skuteczna ale oczywiście mieszamy wtedy kod odpowiedzialny za kontrolkę trybu "runtime" z zachowaniem harakterystycznym dla "DesignMode", ale istnieje inna (lepsza, a w każdym bądź razie bardziej skomplikowana metoda). Okazuje się bowiem że nasza klasa dziedzicząca po klasie ControlDesigner dostała za darmo cztery do tego wielce przydatne metody a mianowicie:

Wszystkie te metody są wirtualne dlatego bez problemu je przykryjemy we własnej klasie oraz wszystkie te metody w parametrze dostają listę właściwości którymi możemy sobie manipulować. Elementy na liście możemy sobie dodawać, usuwać i edytować a będzie to miało odzwierciedlenie w wizualnej liście właściwości i zdarzeń wyświetlanej w "property Window).

Są po dwie rodzaje metod dla właściwości i zdarzeń wersja "Pre" wywoływna przed utworzeniem listy i metody "Post" wywoływane już po utworzeniu listy. Metody "Pre" bardziej się nadają do dodawania i usuwania właściwości i zdażeń a metody "Post" bardziej do modyfikacji istaniejących już właściwości. Modyfikacja właściwości głównie polega na przypisywaniu nowych atrybutów do konkretnych pozycji listy właściwości i zdarzeń reprezentowanych przez klasy "PropertyDescriptor" i "EventDescriptor".

Poniżej przykłady pokazujący sposób dopisania atrybutu Browsable do określonych właściwości naszej klasy kontrolki:

		protected override void PostFilterProperties(IDictionary properties)
		{
			PropertyDescriptor pd;
			string[] noBrowseProperties = new string[] {
								"Enabled",
								"Height",
								"Width",
								"Visible"
                        };

			for (int i = 0; i < noBrowseProperties.Length; i++)
			{
				pd = properties[noBrowseProperties[i]] as PropertyDescriptor;
				if (pd != null)
				{
					properties[pd.Name] = TypeDescriptor.CreateProperty(pd.ComponentType, pd, new Attribute[2] { new BrowsableAttribute(false), new EditorBrowsableAttribute(EditorBrowsableState.Never) });
				}
			}
		}

Jak widać do konkretnych właściwości dopisać można dowolne atrybuty (np. ich listę) dzięki czamu można dosyć dowolnie zmodyfikować działanie danej właściwości naszej kontrolki. W tym przypadku wybrane właściwości zostaną po prostu w VS ukryte.

Podobnie można postąpić ze zdarzeniami "Events" aby ukryć np. te których w naszej kontrolce nie wykorzystujemy.

		protected override void PostFilterEvents(IDictionary events)
		{
			EventDescriptor evnt;
			string[] noBrowseEvents = new string[] {
                                "DataBinding",
                                "Disposed",
                                "Init"
                        };

			for (int i = 0; i < noBrowseEvents.Length; i++)
			{
				evnt = (EventDescriptor)events[noBrowseEvents[i]];
				if (evnt != null)
				{
					events[noBrowseEvents[i]] = TypeDescriptor.CreateEvent(evnt.ComponentType, evnt, BrowsableAttribute.No);
				}
			}
			base.PostFilterEvents(events);
		}

4.Dodanie do property window nie istniejących w kontrolce właściwości.

Ciekawszą jednak możliwością jest możliwość dodania właściwości, lub zdarzeń które w naszej klasie kontrolki nie występują. Po co to komu można zapytać bo wydaje się to mało logiczne i przydatne. A no np. po to aby móc wywołać jakąś formatkę której ustawienia będą operować na więcej niż jednym polu lub będą wykonywać jakieś określone działania na całej kontrolce tak jak w poniższym przykładzie:

				protected override void PreFilterProperties(IDictionary properties)
				{
					base.PreFilterProperties(properties);
					properties["Align"] = TypeDescriptor.CreateProperty(
						this.GetType(),        // the type this property is defined on
						"Align",    // the name of the property
						typeof(controlAlignment),        // the type of the property
						new Attribute[] { new CategoryAttribute("Design"), new EditorAttribute(typeof(ControlAlignUIEditor), typeof(System.Drawing.Design.UITypeEditor)) });    // attributes
				}

Gdzie do kontrolki dodano sztuczną właściwość "Align" która będzie kontrolować położenie wybranych w designerze kontrolek. Nie zagłębiając się jednak zbyt głęboko w ten akurat przykład (miałem coś takiego w jednym z moich projektów) widać że do okna właściwości dodano pozycję "Align" która w sekcji wartości będzie miała formatkę reprezentowaną przez edytor typów właściwości opisany "custom"ową klasą "ControlAlignUIEditor" dziedziczącą zresztą po klasie "UITypeEditor". To że do właściwości jest dodany edytor widać przez to że tworząc właściwość "Align" dodaliśmy do niej dwa atrybuty, jeden to atrybut "Category" opusujący kategorię w której będzie na liście właściwości występowała dana właściwość (nie jest to wymagene przez edytor ale ładnie wygląda), ale drugim ważniejszym atrybutem jest atrybut "EditorAttribute" dzięki któremu możemy opisać jaka klasa (w tym wypadku formatka winForm) opisuje to co zobaczymy po wybraniu wartości właściwości "Align". Oczywiście dowolny edytor możemy dodać (przez atrybut EditorAttribute) do już jakiejść istniejącej właściwości a jedynie w tym przypadku został wykorzystany do zobrazowania faktu że do listy właściwości kontrolki można dodać zupełnie nową obcą właściwość nie występującą we właściwościach danej kontrolki. W ten sposób możemy zaprogramować całkiem sporą funkcjonalność (zaszytą w designMode) wykorzystaywaną tylko na etapie projektowania jakiejś formatki.

5.Dodanie do property window nie istniejącego w kontrolce zdarzenia (event).

Tak samo jak można dodać do listy zdarzeń kontrolki nie istniejącą właściwość, podobnie można dodać zdażenie które fizycznie nie istnieje w kontrolce. Czyli uszczegółowiając nie ma w kontrolce wpisu o zdarzeniu np. nie ma czegoś takiego "public event EventHandler Click;". Normalnie takie istniejące zdarzenie wyświetla się na liscie zdarzeń kotnrolki w designerze (property window), ale jeśli z różnych powodów nie mogliśmy do kontrolki dodać zdażeń ponieważ np. w naszym systemie to nie kontrolki przechowują swoje zdarzenia ale np. zdarzenia są przechowywane w jakimś centralnym miejscu zarządzanym np. przez jakiegoś managera zdarzeń a my potrzebujemy zarządzać zdarzeniami z poziomu designera to dodanie takiego sztucznego zdażenia można zrobić np. tak.

        protected override void PreFilterEvents(IDictionary events)
        {
			EventDescriptor ed_click = TypeDescriptor.CreateEvent(typeof(MyControl), "Click", typeof(ActionFunc), new DesignOnlyAttribute(true), new BrowsableAttribute(true), new MergablePropertyAttribute(false));
			CustomEventDescriptor ced_click = new CustomEventDescriptor(ed_click, typeof(ActionFunc));
			events.Add("Click", ced_click);

			base.PreFilterEvents(events);
        }

Niestety występuje tu pewna niedogodność mianowicie podczas tworzenia przy pomocy metody "TypeDescriptor.CreateEvent" elementu klasy EventDescriptor okazuje się że utworzony obiekt jest wadliwy mianowicie jego właściwości "ComponentType" i "EventType" przy próbie odczytu generują wyjątek który powoduje że cała lista zdarzeń nie będzie widoczna na liście zdarzeń w designerze. Aby pominąć tę niedogodność zastosowałem dodatkową klasę CustomEventDescriptor która niejako pośredniczy pomiędzy etapem tworzenia eventu a atapem dodawania naszego nowego zdarzenia do listy zdarzeń "events.Add()". klasa "CustomEventDescriptor" powoduje że problemowe wartości są ustawiane właściwymi wartościami więc w czasie dodawania nowego zdarzenia do kolekcji zdarzeń wyjątki nie występują. Oto kod klasy "CustomEventDescriptor":

	
	            internal sealed class CustomEventDescriptor : EventDescriptor
                {
                        private readonly Type eventTypeAlias;
                        private readonly Type componentType;
                        public CustomEventDescriptor(EventDescriptor descr, Type aType) : base(descr, null)
                        {
                                componentType = descr.ComponentType;
                                eventTypeAlias = aType;
                        }
                        public CustomEventDescriptor(EventDescriptor descr, Type aType, Type cType) : base(descr, null)
                        {
                                componentType = cType;
                                eventTypeAlias = aType;
                        }
                        public override Type ComponentType{get { return componentType; }}
                        public override Type EventType{get { return eventTypeAlias; }}
                        public override bool IsMulticast { get { return false; } }
                        public override void AddEventHandler(object component, Delegate value){}
                        public override void RemoveEventHandler(object component, Delegate value){}
                }

Jak widać klasa ta niczego specjalnego nie robi poza ustawianiem właściwości componentType i eventTypeAlias które po wygenerowaniu pierwotnej klasy przez procedurę CreateEvent są nie ustawione i próby ich odczytania przez designera generują wyjątek powodujący nie wypełnienie całej listy wyjątków.

6. Własne zdarzenia w designerze oraz wykorzystanie serwisów

Skoro już pokazaliśmy że można dodać własne nie istniejące w kontrolce zdażenie do listy zdarzeń kontrolki w designerze to można się już np. przestać ograniczać domyślnym działaniem jakie designer robi w momencie dwuklika na danym zdażeniu. Otóż jak wiadomo jeśli nie mamy podpiętej do zdarzenia (eventu) żadnej procedury obsługi tego zdarzenia to po dwukliku w takie zdarzenie designer wygeneruje nam w naszym pliku *.aspx.cs domyślną procedurę do zdarzenia z nazwą składającą się z nazwy zdarzenia a do tagu w pliku *.aspx doda atrybut przypisujący to zdarzenie do elementu Tag opisującego naszą kontrolkę.

I tu możemy mieć pewien problem jeśli w naszym customowym systemie mamy zupełnie przepisaną obsługę zdarzeń i nie korzystamy z mechanizmów aspx tylko mamy zrobioną własną obsługę zdarzeń po stronie serwera i klienta. W takim wypadku po dwukliku na zdarzeniu powinniśmy wygenerować własną procedurę o naszej własnej nazwie a może również przydało by się coś jeszcze dodatkowego dopisywać do pliku *.aspx.cs np. jakieś procedury dodające event do naszego managera zdarzeń etc. W każdym bądź razie możemy nie chcieć aby designer generował za nas nazwę procedury bo chcemy mieć ją inną albo nawet możemy nie chcieć aby jakąkolwiek procedurę sam nam dopisywał do pliku *.aspx.cs bo chcemy to sami zrobić.

W takim wypadku możemy podmienić tzw. serwis o nazwie "IEventBindingService".

Tu muszę napisać pewne wyjaśnienie, w designerze działa bardzo dużo takich serwisów które tak na prawdę są klasami odpowiednich typów. Klasy te wykonują pewne określonego rodzaju usługi dla designera każda z tych klas odpowiada za swojego typu działanie i tak np. przykładowo klasa implementująca interfejs IEventBindingService odpowiada właśnie za wyświetlanie i obsługę listy zdarzeń wyświetlanych w "property window" są tam różne metody np. metoda generujące nazwę zdażenia o nazwie "CreateUniqueMethodName" jest tam metoda do pobierania listy zdażeń o nazwie "GetEventProperties" jest tam w końcu kilka metod o wspólnej nazwie "ShowCode" które służą do wygenerowania procedur danego zdarzenia które to będą zapisane do pliku *.aspx.cs. (Uważny czytelnik pewnie zauważy że dodanie eventa do listy eventów może być również zrealizowane przez podmienienie w designerze serwisu na naszą klasę i przykrycie w tej klasie metody "GetEventProperties" tak aby zwracała więcej eventów niż klasa bazowa).

Natomiast np. serwis implementujący interfejs "ISelectionService" odpowiada za operacje wykonywane w momęcie wybierania kontrolek i mając przejęty taki serwis możemy np. podczepić własną procedurę pod zdarzenie serwisu "SelectionChanged" dzięki czemu możemy wykonywać własne działania w momęcie wybierania kontrolek w designerze.

Bardzo przydatnym serwisem jest serwis opisany interfejsem "IComponentChangeService" Przejęcie tego serwisu i podpięcie się pod jego metodę "ComponentChanged" (jest również wersja sprzed zmiany czyli "ComponentChanging") umożliwi nam zarządzanie działaniami jakie designer wykonuje na kontrolkach w czasie zmiany jakiś właściwości kontrolki w oknie "Property window".

Czuję się w tym momęcie zobowiązany złożyć wyjaśnienie czym różni się metoda "ComponentChanged" serwisu "IComponentChangeService" od virtualnej metody "OnComponentChanged" odziedziczonej przez nas po klasie ControlDesigner. Różnica polega głównie na tym że klasa "OnComponentChanged" z designera jest wywoływana tylko na jednej kontrolce tej właśnie której właściwość (property) w "Windows property" zmieniamy. Natomiast klasa serwisu jest wywoływana dla wszystkich kontrolek jakie są w tym widoku i to do nas należy rozpoznanie czy zmienialiśmy tę właśnie kontrolkę której wywołanie klasy zdarzenia nastąpiło można to zrobić np. tak:

	void ChangeService_ComponentChanged(object sender, ComponentChangedEventArgs e)
        {
            if (e.Component is MyControl && ((MyControl)e.Component).ID == ((MyControl)this.Component).ID)
            {
                if (e.Member.Name.Equals("Click"))
					//costam
			}
		}

Jak widać ponieważ w tym akurat przypadku możemy nie chcieć wykonywać pewnych operacji na wszystkich kontrolkach (bo mogą np. nie mieć potrzebnych nam właściwości czy metod dlatego sprawdzamy czy na pewno komponent dla którego wywołano tę metodę "e.Component" pochodzi na pewno od klasy naszej kontrolki "e.Component is MyControl" a jeśli petrzebujemy już tylko znaleść wywołanie tej metody dla na pewno kontrolki której zmiany dotyczą można również porównać unikatowy identyfikator kontrolki dla krórej wywołano zdarzenie z podstawową kontrolką krórą obsługuje ta akurat klasa designera "((MyControl)e.Component).ID == ((MyControl)this.Component).ID".

Ciekawą (i przydatną) właściwością jest to że metoda "ChangeService_ComponentChanged" zostanie wywołana zarówno przy zmianie wartości właściwości (property) jak i przy zmianie zdarzenia (event) i przez rozpoznanie nazwy właściwości "if (e.Member.Name.Equals("CHANGE"))" musimy rozpoznać z czym mamy do czynienia.

Gdzieś to w tekście powyżej już się przewijało ale dla jasności napiszę że serwisy można właściwie wykorzystywać na dwa podstawowe sposoby.

Można pobrać taki serwis (oczywiście referencję do niego) przy pomocy metody designera "GetService" robi się to np. tak:

IComponentChangeService changeService = GetService(typeof(IComponentChangeService)) as IComponentChangeService;

A mając już adres serwisu można do jego eventu "ComponentChanged" przypisać własną metodę obsługi która właśnie się wywoła w momęcie zmieny właściwości w oknie "Properry Window" (ale na wszystkich kontrolkach więc jeśli potrzebujemy tylko zmian w jednej kontrolce zalecam używać metody OnComponentChanged designera)

Podpięcie własnej metody do serwisu może wyglądać np. tak:

		
		if (this.changeService != null)
        {
			this.changeService.ComponentChanged += new ComponentChangedEventHandler(ChangeService_ComponentChanged);
        }

A metoda przypięta do eventu ComponentChanged może wyglądać tak:

		
        void ChangeService_ComponentChanged(object sender, ComponentChangedEventArgs e)
        {
			//jakieś operacje
		}

Drugim sposobem na wykorzystanie serwisów jest zupełne podmienienie całego serwisu na naszą klasę dziedziczącą po tym serwisie można to zrobić np. tak:

		
		IEventBindingService eventBindingService = GetService(typeof(IEventBindingService)) as IEventBindingService;
		if (this.eventBindingService != null)
		{
			Type type = typeof(IEventBindingService);
			host.RemoveService(type);
			host.AddService(type, new myEvBinServ(this.eventBindingService));
		}

W tym wypadku podstawiamy własną klasę "myEvBinServ" która to klasa będzie odpowiadała za to aby domyślne metody dla zdażeń w pliku nie były generowane czyli wszystkie metody ShowCode mają zwracać wartość false;

		
		public class myEvBinServ : IEventBindingService
		{
		private IEventBindingService _eventBindingService;
		public myEvBinServ(IEventBindingService eventBindingService)
		{
			_eventBindingService = eventBindingService;
		}
		public String CreateUniqueMethodName(IComponent component, EventDescriptor e)
		{
			return String.Format("On{0}_{1}", ((MyControl)component).ID, e.Name);
		}
		public ICollection GetCompatibleMethods(EventDescriptor e)
		{
			ICollection col = _eventBindingService.GetCompatibleMethods(e);
			return col;
		}
		public EventDescriptor GetEvent(PropertyDescriptor property)
		{
			return _eventBindingService.GetEvent(property);
		}
		public PropertyDescriptorCollection GetEventProperties(EventDescriptorCollection events)
		{
			return _eventBindingService.GetEventProperties(events);
		}
		public PropertyDescriptor GetEventProperty(EventDescriptor e)
		{
			return _eventBindingService.GetEventProperty(e);
		}
		public bool ShowCode()
		{
			return false;
			//return _eventBindingService.ShowCode();
		}
		public bool ShowCode(int lineNumber)
		{
			return false;
			//return _eventBindingService.ShowCode(lineNumber);
		}
		public bool ShowCode(IComponent component, EventDescriptor e)
		{
			return false;
			//return _eventBindingService.ShowCode(component, e);
		}
	}

Do konstruktora klasy przekazujemy orginalny serwis po to aby w niektórych przypadkach móc wywoływać orginalny serwis przykryty naszą nakładką. Będzie tak np. w przypadku jeśli chcielibyśmy aby ciała metod w pliku *.aspx.cs były generowane a tylko zależało by nam na zmianie nazwy tych metod w takim wypedku trzeba by zrezygnować ze zwracania wartości false przez metody ShowCode i odkomentować wywołanie orginalnego serwisu. Jeśli tak zrobimy właściwe zastosowanie znajdzie również wtedy przykrycie przez nas metody "CreateUniqueMethodName" która generuje naszą nazwę metody, można w ten sposób zarządzać jej nazwą. Ale w przypadku jeśli w ogóle rezygnujemy z generacji ciał metod zdarzeń to i generacja nazwy nie będzie wykorzystywana i trzeba będzie samemu w innym miejscu tworzyć nazwę metody.

7. Generacja własnej procedury obsługi w pliku aspx.

Skoro juz w poprzednim przykładzie zablokowaliśmy generację metod zdarzeń kontrolki przez designer, ponieważ chcieliśmy mieć własne zupełnie inne metody dodane do pliku *.aspx.cs to teraz musimy jakoś sami oprogramować modyfikację pliku zdefinowanego w "codeBehind" (*.aspx.cs).

Prawdopodobnie dało by się to zrobić w tej naszej klasie implementującej interfejs "IEventBindingService" którą podmieniliśmy serwis w designerze prawdopodobnie trzeba by napisać własne procedury "ShowCode" jednak z uwagi że takie podejście mi po prostu nie wychodziło zadanie to zrealizowałem inaczej. Mianowicie po pobraniu serwisu "IComponentChangeService" i podpięciu się pod jego zdażenie "ComponentChanged" sprawdzam czy nastąpiła zmiana właściwośći zdażenia w "property Window" i jeśli tak to przy pomocy obiektu do automatyzacji Visual Studio "EnvDTE" dobieram się do odpowiedniego pliku z projektu dopisując do niego odpowiedni kod.

Oto moja metoda do przejmowania zmian we właściwościach.

	
		private void ChangeService_ComponentChanged(object sender, ComponentChangedEventArgs e)
		{
			if (e.Component is MyControl && ((MyControl)e.Component).ID == ((MyControl)this.Component).ID)
			{
				if (e.Member.Name.Equals("Click"))
				{
					CreateMethodEventBody(e.Member.Name, ((MyControl)e.Component).ID, e.NewValue.ToString());
				}		
			}
		}

Jak widać jedyną nowością w porównaniu do poprzednio przedstawionych wersji tej metody jest wywołanie funkcji "CreateMethodEventBody" która to funkcja przy pomocy obiektu "EnvDTE" (do automatyzacji czynności w Visual Studio) modyfikuje odpowiedni plik projektu. Można to działanie napisać w dowolny sposób wybrany przez konkretnego programistę więc jeśli ktoś lubi parsowanie plików z kodami źródłowymi to może to robić zupełnie na strumieniach plikowych, ja wykorzystałem obiekt "EnvDTE" ponieważ obiekt ten umożliwia poruszanie się po strukturze kodu (w tym wypadku C#) jak po drzewie co jest niesamowitym ułatwieniem przy konstruowaniu takiego algorytmu. Oto kod to robiący:

		
		protected void CreateMethodEventBody(string eventName, string ID, string methodName)
		{
			this.CreateMethodEventBody(eventName, vsCMTypeRef.vsCMTypeRefVoid, ID, methodName);
		}

		protected void CreateMethodEventBody(string eventName, vsCMTypeRef returnType, string ID, string methodName)
		{
			EventDescriptorCollection eventColl = TypeDescriptor.GetEvents(this.Component, new Attribute[0]);
			PropertyDescriptor epd;
			if (eventColl != null)
			{
				EventDescriptor ed = eventColl[eventName] as EventDescriptor;
				if (ed != null)
				{
					//string methodName = this.eventBindingService.CreateUniqueMethodName(this.Component, ed);
					epd = this.eventBindingService.GetEventProperty(ed);

					EnvDTE.DTE dte = (EnvDTE.DTE)GetService(typeof(EnvDTE.DTE));
					string name = dte.ActiveDocument.Name;
					string path = dte.ActiveDocument.Path;
					TextDocument objAspxDoc = dte.ActiveDocument.Object("TextDocument") as EnvDTE.TextDocument;
					EditPoint objEP = objAspxDoc.StartPoint.CreateEditPoint();
					EditPoint objEndEP = objAspxDoc.EndPoint.CreateEditPoint();
					string calyAspx = objEP.GetText(objEndEP);

					string codeBehind = string.Format("{0}.cs", dte.ActiveDocument.Name);
					string[] nameParts = dte.ActiveDocument.Name.Split(new string[] { "." }, StringSplitOptions.None);
					string inherits = nameParts.First();

					try
					{
						string startTag = "<%@ Page";
						string endTag = "%>";
						int startIndex = calyAspx.IndexOf(startTag);
						int endIndex = calyAspx.IndexOf(endTag, startIndex) + endTag.Length;
						string contentAspx = calyAspx.Substring(startIndex, endIndex - startIndex);
						contentAspx = contentAspx.Replace("%@ ", "");
						contentAspx = contentAspx.Replace("%>", "/>");
						XmlDocument xmlDoc = new XmlDocument();
						xmlDoc.LoadXml(contentAspx);


						if (xmlDoc.FirstChild.NodeType == XmlNodeType.Element && xmlDoc.FirstChild.Name == "Page")
						{
							codeBehind = xmlDoc.FirstChild.Attributes["CodeBehind"].Value;
							inherits = xmlDoc.FirstChild.Attributes["Inherits"].Value;
							string[] inhParts = inherits.Split(new string[] { "." }, StringSplitOptions.None);
							inherits = inhParts.Last();
						}
					}
					catch (Exception) { }
					//now we try to find item/file with codeBehind "aspx.cs"
					ProjectItem csItem = null;
					foreach (ProjectItem pitem in dte.ActiveDocument.ProjectItem.ProjectItems)
					{
						if (pitem.Name == codeBehind)
						{
							csItem = pitem;
							break;
						}
					}
					//FileCodeModel fileCM = csItem.FileCodeModel;
					CodeElements ces = csItem.FileCodeModel.CodeElements;
					CodeNamespace cns = findCodeElement(ces, "DesignerPresentation", vsCMElement.vsCMElementNamespace) as CodeNamespace;

					if (cns == null)
						return;
					ces = cns.Members;
					if (ces == null)
						return;
					CodeClass cls = findCodeElement(ces, inherits, vsCMElement.vsCMElementClass) as CodeClass;
					if (cls == null)
						return;

					ces = cls.Members;

					//point of code where we jump and show code
					TextPoint tp = cls.GetEndPoint(vsCMPart.vsCMPartBody);
					EditPoint ep = tp.CreateEditPoint();

								CodeFunction cf = findCodeElement(ces, methodName, vsCMElement.vsCMElementFunction) as CodeFunction;
								if (cf == null) //We add method only if it no body
								{
									cf = cls.AddFunction(methodName, vsCMFunction.vsCMFunctionFunction, returnType, -1, vsCMAccess.vsCMAccessPublic);
									tp = cf.GetStartPoint(vsCMPart.vsCMPartBody);
									ep = tp.CreateEditPoint();
									ep.Indent();
									ep.Insert("//Tu proszę wpisywać kod obsługi metody");
									if (cf != null)
									{
										epd.SetValue(this.Component, methodName);
									}
								}
					Window win = dte.ItemOperations.OpenFile(dte.ActiveDocument.Path + csItem.Name);
					if (win != null)
					{
						TextDocument objTextDoc = dte.ActiveDocument.Object("TextDocument") as EnvDTE.TextDocument; //after openFile active document become cs file with code (previous active was aspx file)
						objTextDoc.Selection.GotoLine(ep.Line);
					}
				}
			}
		}
		private CodeElement findCodeElement(CodeElements ces, string elementName, vsCMElement elementType)
		{
			CodeElement ce = null;
			foreach (CodeElement cec in ces)
			{
				if (cec.Kind == elementType)
					if (cec.Name == elementName)
					{
						ce = cec as CodeElement;
						break;
					}
			}
			return ce;
		}

Podałem ten kod jako przykład, jako jakąś możliwość do wzorowania się, oczywiste jest że każdy będzie musiał stworzyć tą procedurę dokładnie pod swój projekt tak aby działanie kodu dopisującego coś do pliku "CodeBehind" (*.aspx.cs) odzwierciedlało strukturę rozwiązania zastosowanego w danym projekcie.

Designer wbudowany w Visual Studio ma dosyć szerokie możliwości i opisanie ich w tym krótkim artykule było by tródne, tym bardziej tródne że ja w trakcie pracy nad projektem gdzie odkrywałem meandry Designera po prostu ich wszystkich nie odkryłem, wykorzystałem to co było mi akurat potrzebne dla realizacji postawionego przede mną zadania. Niektórych dosyć oczywistych rzeczy takich jak tworzenie podręcznych menu "ActionLists", "DesignerVerbCollection" czy tworzenia edytorów UITypeEditor nie opisywałem bo jest to dosyć intuicyjne a zresztą jest na to sporo przykładów w necie a raczej starałem się opisać to co mi sprawiało tródność a co nie jest zbyt standardową operacją designera (jak na przykład podmiana serwisu czy dodanie nieistniejącego eventa). Pozdrawim innych designerowych samurajów w ich trudnej i nierównej walce z designerem.

8 .Załączniki

Projekt w Visual Studio 2010 prezentujący prostą kontrolkę użytkownika i jej reprezentację w designerze Designer presentation



Dodaj komentarz:
Tak
Nie

Autor:Eric
Data: 2021-10-19 19:13
Treść:My name’s Eric and I just found your site extern.pl. It’s got a lot going for it, but here’s an idea to make it even MORE effective. Talk With Web Visitor – CLICK HERE https://talkwithwebvisitors.com for a live demo now. Talk With Web Visitor is a software widget that’s works on your site, ready to capture any visitor’s Name, Email address and Phone Number. It signals you the moment they let you know they’re interested – so that you can tal

Autor:Kendra
Data: 2021-10-07 22:05
Treść:Hi , Who would I contact at your company that handles ordering your logo products, t-shirts, and promotional items? For over 20 years we have been creating and supplying our clients with all their custom logo merchandise. We can put your logo onto anything including: -Custom Printed T-shirts / Hoodies -Pens -Mugs -T-shirts -Bags -Banners -Table Covers -Key chains -USB flash drives

Autor:Eric
Data: 2021-10-06 23:52
Treść:Cool website! My name’s Eric, and I just found your site - extern.pl - while surfing the net. You showed up at the top of the search results, so I checked you out. Looks like what you’re doing is pretty cool. But if you don’t mind me asking – after someone like me stumbles across extern.pl, what usually happens? Is your site generating leads for your business? I’m guessing some, but I also bet you’d like more… studies show that

Autor:Johan
Data: 2021-09-30 21:11
Treść:Hello, My name is Johan, I am a PHP programmer that specializes in data driven web applications. Anything related to PHP, MySQL, Data scraping etc. If you have any custom jobs you can add me on skype to discuss your requirements. Skype: cmsdevelopers Regards, Johan

Autor:Eric
Data: 2021-09-23 23:38
Treść:My name’s Eric and I just found your site extern.pl. It’s got a lot going for it, but here’s an idea to make it even MORE effective. Talk With Web Visitor – CLICK HERE https://talkwithwebvisitors.com for a live demo now. Talk With Web Visitor is a software widget that’s works on your site, ready to capture any visitor’s Name, Email address and Phone Number. It signals you the moment they let you know they’re interested – so that you can tal

Autor:Madie
Data: 2021-09-11 13:16
Treść:Hello from SunDataGroup.com We are selling leads from around the world. I thought your company could benefit from it. You can visit our website www.SunDataGroup.com to see some of our data. We have a special offer running. All our databases for $99.

Autor:Eric
Data: 2021-09-11 8:56
Treść:My name’s Eric and I just found your site extern.pl. It’s got a lot going for it, but here’s an idea to make it even MORE effective. Talk With Web Visitor – CLICK HERE http://talkwithcustomer.com for a live demo now. Talk With Web Visitor is a software widget that’s works on your site, ready to capture any visitor’s Name, Email address and Phone Number. It signals you the moment they let you know they’re interested – so that you can talk to

Autor:Eric
Data: 2021-09-09 23:58
Treść:My name’s Eric and I just found your site extern.pl. It’s got a lot going for it, but here’s an idea to make it even MORE effective. Talk With Web Visitor – CLICK HERE http://talkwithcustomer.com for a live demo now. Talk With Web Visitor is a software widget that’s works on your site, ready to capture any visitor’s Name, Email address and Phone Number. It signals you the moment they let you know they’re interested – so that you can talk to

Autor:Rosetta
Data: 2021-09-02 5:40
Treść:Hi from Order-Fulfillment.net Who would I speak with at your company about any of your order fulfillment and drop shipping needs? My company Order-Fulfillment.net can warehouse, inventory, and manage your drop shipping / order fulfillment for your customer orders. Based in the USA for almost 2 decades, near major USA air and sea ports. Here are some of the items we currently pick, pack and ship for clients: -Books, training manua

Autor:Eric
Data: 2021-09-02 5:05
Treść:Cool website! My name’s Eric, and I just found your site - extern.pl - while surfing the net. You showed up at the top of the search results, so I checked you out. Looks like what you’re doing is pretty cool. But if you don’t mind me asking – after someone like me stumbles across extern.pl, what usually happens? Is your site generating leads for your business? I’m guessing some, but I also bet you’d like more… studies show that

Autor:Cynthia
Data: 2021-08-27 14:22
Treść:We have an amazing database of leads for you. All countries are $99 and you can buy the entire world 165 countries for $179. This offer is valid till Friday. www.SunDataGroup.one

Autor:Eric
Data: 2021-08-18 5:51
Treść:My name’s Eric and I just found your site extern.pl. It’s got a lot going for it, but here’s an idea to make it even MORE effective. Talk With Web Visitor – CLICK HERE https://talkwithwebvisitors.com for a live demo now. Talk With Web Visitor is a software widget that’s works on your site, ready to capture any visitor’s Name, Email address and Phone Number. It signals you the moment they let you know they’re interested – so that you can tal

Autor:QGWD8HANY8
Data: 2021-08-16 11:41
Treść:QGWD8HANY80VX8SWXF www.google.com Where are you located ? I want to come to you one of these days

Autor:Eric
Data: 2021-08-10 7:05
Treść:My name’s Eric and I just found your site extern.pl. It’s got a lot going for it, but here’s an idea to make it even MORE effective. Talk With Web Visitor – CLICK HERE https://talkwithwebvisitors.com for a live demo now. Talk With Web Visitor is a software widget that’s works on your site, ready to capture any visitor’s Name, Email address and Phone Number. It signals you the moment they let you know they’re interested – so that you can tal

Autor:Paweł
Data: 2021-07-07 10:09
Treść:Dzień dobry, czy zastanawiali się Państwo nad tym, aby odświeżyć wizerunek firmy w sieci lub rozpocząć sprzedaż w sklepie internetowym? Jeśli tak, to chętnie w tym pomożemy. Tworzymy sklepy i strony internetowe już od wielu lat. Nasza oferta jest kompleksowa i atrakcyjna finansowo. Wyręczamy naszych klientów w wielu aspektach a ich zaangażowanie, jeśli sobie tego życzą, ogranicza się do minimum. Jeśli są Państwo zainteresowani, to proszę o odpowi

Autor:Charley
Data: 2021-06-30 23:06
Treść:Hi , I am following up on my message below. Who would I speak with about handling your US order fulfillment and shipping? Regards, Charley order-fulfillment.net ------------------------------------------------------------------------ Hi, Who would I speak with at your company that manages your product shipping and order fulfillment? We are US company, offering warehousing, order fulfillment an

Autor:Michel
Data: 2021-06-28 20:42
Treść:It is with sad regret to inform you that because of the Covid pandemic BestLocalData.com is shutting down at the end of the month. We have lost family members and colleagues and have decided to shut down BestLocalData.com It was a pleasure serving you all these years. We have made all our databases available for $99 (All of it for $99) for those interested. Kind Regards, BestLocalData.com Michel

Autor:Eric
Data: 2021-06-23 17:54
Treść:Hey, this is Eric and I ran across extern.pl a few minutes ago. Looks great… but now what? By that I mean, when someone like me finds your website – either through Search or just bouncing around – what happens next? Do you get a lot of leads from your site, or at least enough to make you happy? Honestly, most business websites fall a bit short when it comes to generating paying customers. Studies show that 70% of a site’s visitors disappear and are

Autor:Iola
Data: 2021-06-22 7:15
Treść:The plug welcomes you with a super discount of 15% on every product for a limited period of time! (Promo code: THEPLUG) We offer the largest assortment of private databases! Such as: - The Largest collection of Linkedin Databases! - Forex Consumers (worldwide) - Casino Consumers (worldwide) - Business Databases (worldwide) - Cryptocurrency consumers (worldwide) - many more.. And the best part of it! We are the only source

Autor:Miquel
Data: 2021-06-19 16:05
Treść:Hello from FbCourses.net Want to pay $0.01 a click? We got you covered. A great team of Global Digital Marketing experts have compiled this list of 13 Best + Free Facebook Advertising Training, Classes and Courses to help you learn and excel at Facebook Ads & Marketing. Thousands of professionals have already benefited from this list on Facebook Training. Regards, Miquel

Autor:Eric
Data: 2021-06-19 7:20
Treść:My name’s Eric and I just found your site extern.pl. It’s got a lot going for it, but here’s an idea to make it even MORE effective. Talk With Web Visitor – CLICK HERE http://talkwithcustomer.com for a live demo now. Talk With Web Visitor is a software widget that’s works on your site, ready to capture any visitor’s Name, Email address and Phone Number. It signals you the moment they let you know they’re interested – so that you can talk to

Autor:Eric
Data: 2021-06-16 10:28
Treść:My name’s Eric and I just found your site extern.pl. It’s got a lot going for it, but here’s an idea to make it even MORE effective. Talk With Web Visitor – CLICK HERE http://talkwithcustomer.com for a live demo now. Talk With Web Visitor is a software widget that’s works on your site, ready to capture any visitor’s Name, Email address and Phone Number. It signals you the moment they let you know they’re interested – so that you can talk to

Autor:Carlton
Data: 2021-06-15 11:05
Treść:Hello from order-fulfillment.net, Doing your own product shipping or order fulfillment in house? Tired of it? Visit us on www.order-fulfillment.net We can store, inventory, and manage your drop shipping / order fulfillment for you. Based in the US for almost 2 decades - we ship around the world and will save you time and money. Who would be the best contact at your company to discuss? Here are some of the items we

Autor:Eric
Data: 2021-06-15 4:03
Treść:My name’s Eric and I just found your site extern.pl. It’s got a lot going for it, but here’s an idea to make it even MORE effective. Talk With Web Visitor – CLICK HERE https://talkwithwebvisitors.com for a live demo now. Talk With Web Visitor is a software widget that’s works on your site, ready to capture any visitor’s Name, Email address and Phone Number. It signals you the moment they let you know they’re interested – so that you can tal

Autor:Stanton
Data: 2021-06-11 9:41
Treść:We at BestLocalData.com has been hit badly by Covid-19 and as a result BestLocalData.com is shutting down. We provided the best data to companies to find their right customer base, we don't want other companies to go down the same path we went and go out of business. As a result we are providing our data till the end of the week at the lowest possible prices. BestLocalData.com

Autor:Reta
Data: 2021-06-02 20:53
Treść:Hello, It is with sad regret to inform you that BestLocalData.com is shutting down. We have made all our databases for sale for a once-off price. Visit our website to get the best bargain of your life. BestLocalData.com Regards, Reta

Autor:►►►
Data: 2021-06-02 1:26
Treść:►►► ✅ Attention! Don't miss out on new opportunities https://is.gd/Hwg6KC

Autor:Elmo
Data: 2021-05-24 16:19
Treść:Hello, It is with sad regret to inform you that BestLocalData.com is shutting down. We have made all our databases for sale for a once-off price. Visit our website to get the best bargain of your life. BestLocalData.com Regards, Elmo

Autor:Eric
Data: 2021-05-21 7:02
Treść:Hey, this is Eric and I ran across extern.pl a few minutes ago. Looks great… but now what? By that I mean, when someone like me finds your website – either through Search or just bouncing around – what happens next? Do you get a lot of leads from your site, or at least enough to make you happy? Honestly, most business websites fall a bit short when it comes to generating paying customers. Studies show that 70% of a site’s visitors disappear and are

Autor:Eric
Data: 2021-05-20 15:29
Treść:My name’s Eric and I just found your site extern.pl. It’s got a lot going for it, but here’s an idea to make it even MORE effective. Talk With Web Visitor – CLICK HERE http://talkwithcustomer.com for a live demo now. Talk With Web Visitor is a software widget that’s works on your site, ready to capture any visitor’s Name, Email address and Phone Number. It signals you the moment they let you know they’re interested – so that you can talk to

Autor:Eric
Data: 2021-05-16 11:15
Treść:My name’s Eric and I just found your site extern.pl. It’s got a lot going for it, but here’s an idea to make it even MORE effective. Talk With Web Visitor – CLICK HERE http://talkwithcustomer.com for a live demo now. Talk With Web Visitor is a software widget that’s works on your site, ready to capture any visitor’s Name, Email address and Phone Number. It signals you the moment they let you know they’re interested – so that you can talk to

Autor:Leon
Data: 2021-05-16 5:09
Treść:Hello from order-fulfillment.net, Doing your own product shipping or order fulfillment in house? Tired of it? Visit us on www.order-fulfillment.net We can store, inventory, and manage your drop shipping / order fulfillment for you. Based in the US for almost 2 decades - we ship around the world and will save you time and money. Who would be the best contact at your company to discuss? Here are some of the items we

Autor:Callie
Data: 2021-05-11 10:27
Treść:Hello from SendBulkMails.com, We have a special limited offer for you to send unlimited emails. We allow non-permission based emails and you won't ever get blocked. We also buy your domain for you and give you a clean IP and setup your DNS records. Check us out on SendBulkMails.com

Autor:Eric
Data: 2021-05-09 14:40
Treść:My name’s Eric and I just found your site extern.pl. It’s got a lot going for it, but here’s an idea to make it even MORE effective. Talk With Web Visitor – CLICK HERE https://talkwithwebvisitors.com for a live demo now. Talk With Web Visitor is a software widget that’s works on your site, ready to capture any visitor’s Name, Email address and Phone Number. It signals you the moment they let you know they’re interested – so that you can tal

Autor:Adalberto
Data: 2021-04-29 1:36
Treść:Hey! BestLocalData has an amazing special. 16 databases for $99. LinkedIn(43million records) USA B2B (28 million companies), Australia, South Africa, UK, Germany and all of that included. Instant delivery! https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=7S34LZQSKKQHG Or check out more on our website! Regards, Adalberto

Autor:https://u.
Data: 2021-04-19 12:14
Treść:The last day of giant discounts on all our products https://u.to/2x5AGw

Autor:Eric
Data: 2021-04-18 19:37
Treść:My name’s Eric and I just found your site extern.pl. It’s got a lot going for it, but here’s an idea to make it even MORE effective. Talk With Web Visitor – CLICK HERE http://talkwithcustomer.com for a live demo now. Talk With Web Visitor is a software widget that’s works on your site, ready to capture any visitor’s Name, Email address and Phone Number. It signals you the moment they let you know they’re interested – so that you can talk to

Autor:Reta
Data: 2021-04-17 11:34
Treść:Do you need clients? We compiled some of the world's top databases for you at ridiculous low prices. $49 for any of our databases or $99 for all 16 databases! Visit BestLocalData.com Regards, Reta

Autor:Maryjo
Data: 2021-04-14 6:49
Treść:Hello, Send unlimited emails to unlimited lists with one click and no monthly fees! $99 once off! LifeMailNow.com

Autor:Eric
Data: 2021-04-13 0:21
Treść:My name’s Eric and I just found your site extern.pl. It’s got a lot going for it, but here’s an idea to make it even MORE effective. Talk With Web Visitor – CLICK HERE https://talkwithwebvisitors.com for a live demo now. Talk With Web Visitor is a software widget that’s works on your site, ready to capture any visitor’s Name, Email address and Phone Number. It signals you the moment they let you know they’re interested – so that you can tal

Autor:Susanne
Data: 2021-04-08 9:05
Treść:Hey, It was nice speaking to you the other day, this is the service I was telling you about that helped us boost our ROI almost 2000% Its a company called Lifemail.studio sorry it took so long to get back to you. They allow you to send any email doesn't matter what. We dealt with a guy named Michael, he was friendly and got us setup really quickly. Regards, Susanne

Autor:Eric
Data: 2021-04-03 10:08
Treść:Hi, my name is Eric and I’m betting you’d like your website extern.pl to generate more leads. Here’s how: Talk With Web Visitor is a software widget that’s works on your site, ready to capture any visitor’s Name, Email address and Phone Number. It signals you as soon as they say they’re interested – so that you can talk to that lead while they’re still there at extern.pl. Talk With Web Visitor – CLICK HERE https://talkwithwebvisitors.com for a l

Autor:Eric
Data: 2021-03-30 21:11
Treść:Hey there, I just found your site, quick question… My name’s Eric, I found extern.pl after doing a quick search – you showed up near the top of the rankings, so whatever you’re doing for SEO, looks like it’s working well. So here’s my question – what happens AFTER someone lands on your site? Anything? Research tells us at least 70% of the people who find your site, after a quick once-over, they disappear… forever. That means th

Autor:Jerilyn
Data: 2021-03-29 18:32
Treść:Need extra income? A fully hosted, done for you content + monetisation and a stunning design web based software that creates a fully automated done for you cryptocurrency affiliate site, Visit us: https://warriorplus.com/o2/a/f5s4y/0

Autor:Eric
Data: 2021-03-29 16:34
Treść:Good day, My name is Eric and unlike a lot of emails you might get, I wanted to instead provide you with a word of encouragement – Congratulations What for? Part of my job is to check out websites and the work you’ve done with extern.pl definitely stands out. It’s clear you took building a website seriously and made a real investment of time and resources into making it top quality. There is, however, a catch… more accu

Autor:Patrice
Data: 2021-03-28 12:18
Treść:Your All In One Solution For Creating All The Content You'll Ever Need. Proprietary AI Turns YouTube Videos Into Traffic Getting Articles At The Press Of A Button! We’ve Been Getting Free Autopilot Traffic From Google Without SEO Experience For Over 2 Years By Converting Others YouTube Videos Into Articles… https://warriorplus.com/o2/a/gmvfs/0

Autor:Tosha
Data: 2021-03-25 7:54
Treść:Hello, BestLocalData.com has a special package you get any group of databases for $49 or $249 for all 16 databases and unlimited emails for a year(Domain, IP, Dashboard included). You can purchase it on BestLocalData.com and see samples if you are interested.

Autor:Eric
Data: 2021-03-10 6:31
Treść:Good day, My name is Eric and unlike a lot of emails you might get, I wanted to instead provide you with a word of encouragement – Congratulations What for? Part of my job is to check out websites and the work you’ve done with extern.pl definitely stands out. It’s clear you took building a website seriously and made a real investment of time and resources into making it top quality. There is, however, a catch… more accu

Autor:Eric
Data: 2021-03-10 2:08
Treść:Hi, my name is Eric and I’m betting you’d like your website extern.pl to generate more leads. Here’s how: Talk With Web Visitor is a software widget that’s works on your site, ready to capture any visitor’s Name, Email address and Phone Number. It signals you as soon as they say they’re interested – so that you can talk to that lead while they’re still there at extern.pl. Talk With Web Visitor – CLICK HERE https://talkwithwebvisitors.com for a l

Autor:Cyril
Data: 2021-03-09 13:42
Treść:Hi, I'm always asked what is the quickest way to make money online, when you are just starting out? Well here's the definitive answer that question: ==> https://sesforyou.com New Book Reveals How I Built A 7-Figure Online Business Using Nothing But Ethical Email Marketing To Drive Revenue, Sales and Commissions... ==> https://sesforyou.com Regards, SesForYou.com

Autor:SendBulkMa
Data: 2021-02-26 3:05
Treść:SendBulkMails.com allows you to reach out to clients via cold email marketing. - 1Mil emails starter package - Dedicated IP and Domain Included - Detailed statistical reports (delivery, bounce, clicks etc.) - Quick and easy setup with extended support at no extra cost. - Cancel anytime! SendBulkMails.com

Autor:Kandice
Data: 2021-02-24 1:27
Treść:Do you need more clients? We have amazing databases starting at $9.99 until the end of the Month! Visit us at StarDataGroup.com

Autor:Eric
Data: 2021-02-20 4:57
Treść:Hi, my name is Eric and I’m betting you’d like your website extern.pl to generate more leads. Here’s how: Talk With Web Visitor is a software widget that’s works on your site, ready to capture any visitor’s Name, Email address and Phone Number. It signals you as soon as they say they’re interested – so that you can talk to that lead while they’re still there at extern.pl. Talk With Web Visitor – CLICK HERE https://talkwithwebvisitors.com for a l

Autor:QGWHB5MR0Z
Data: 2021-02-19 9:16
Treść:QGWHB5MR0Z8H11PWXF www.google.com

Autor:Claudio
Data: 2021-02-16 14:27
Treść:Use SendBulkMails.com to run email campaigns from your own private dashboard. Cold emails are allowed and won't get you blocked :) - 1Mil emails / mo @ $99 USD - Dedicated IP and Domain Included - Detailed statistical reports (delivery, bounce, clicks etc.) - Quick and easy setup with extended support at no extra cost. - Cancel anytime! Regards, www.SendBulkMails.com

Autor:Eric
Data: 2021-02-14 18:13
Treść:Hey there, I just found your site, quick question… My name’s Eric, I found extern.pl after doing a quick search – you showed up near the top of the rankings, so whatever you’re doing for SEO, looks like it’s working well. So here’s my question – what happens AFTER someone lands on your site? Anything? Research tells us at least 70% of the people who find your site, after a quick once-over, they disappear… forever. That means th

Autor:Janine
Data: 2021-02-13 11:04
Treść:It is with sad regret to inform you StarDataGroup.com is shutting down. Fire sale going on! Any group of databases listed below is $49 or $149 for all 16 databases in this one time offer. You can purchase it at www.StarDataGroup.com and view samples. - LinkedIn Database 43,535,433 LinkedIn Records - USA B2B Companies Database 28,147,835 Companies - Forex Forex South Africa 113,550 Forex Traders For

Autor:QGWITK4NGH
Data: 2021-02-11 4:45
Treść:QGWITK4NGHRG8QYWXF How to get to you ? I need to get a consultation www.google.com

Autor:Eric
Data: 2021-01-31 10:10
Treść:Hey there, I just found your site, quick question… My name’s Eric, I found extern.pl after doing a quick search – you showed up near the top of the rankings, so whatever you’re doing for SEO, looks like it’s working well. So here’s my question – what happens AFTER someone lands on your site? Anything? Research tells us at least 70% of the people who find your site, after a quick once-over, they disappear… forever. That means th

Autor:Eric
Data: 2021-01-20 13:34
Treść:Hi, Eric here with a quick thought about your website extern.pl... I’m on the internet a lot and I look at a lot of business websites. Like yours, many of them have great content. But all too often, they come up short when it comes to engaging and connecting with anyone who visits. I get it – it’s hard. Studies show 7 out of 10 people who land on a site, abandon it in moments without leaving even a trace. You got the eyeball, but nothi

Autor:Eric
Data: 2021-01-15 13:42
Treść:Hey there, I just found your site, quick question… My name’s Eric, I found extern.pl after doing a quick search – you showed up near the top of the rankings, so whatever you’re doing for SEO, looks like it’s working well. So here’s my question – what happens AFTER someone lands on your site? Anything? Research tells us at least 70% of the people who find your site, after a quick once-over, they disappear… forever. That means th

Autor:Marietta
Data: 2021-01-10 15:54
Treść:Happy New Year!!! See our presents and offer from our team: https://is.gd/u2YqZn

Autor:Eric
Data: 2021-01-02 14:16
Treść:Hi, Eric here with a quick thought about your website extern.pl... I’m on the internet a lot and I look at a lot of business websites. Like yours, many of them have great content. But all too often, they come up short when it comes to engaging and connecting with anyone who visits. I get it – it’s hard. Studies show 7 out of 10 people who land on a site, abandon it in moments without leaving even a trace. You got the eyeball, but nothi

Autor:Eric
Data: 2020-12-30 16:38
Treść:Hey there, I just found your site, quick question… My name’s Eric, I found extern.pl after doing a quick search – you showed up near the top of the rankings, so whatever you’re doing for SEO, looks like it’s working well. So here’s my question – what happens AFTER someone lands on your site? Anything? Research tells us at least 70% of the people who find your site, after a quick once-over, they disappear… forever. That means th

Autor:Barbara
Data: 2020-12-12 17:50
Treść:DOMAIN SERVICES EXPIRATION NOTICE FOR extern.pl Domain Notice Expiry ON: Dec 12, 2020 We have not obtained a settlement from you. We've tried to email you yet were incapable to contact you. Visit: https://domainlogging.com/?web=extern.pl For info as well as to make a discretionary payment for your domain website solutions. 121220201150383753688578798extern.pl

Autor:Courtney
Data: 2020-12-06 19:48
Treść:Cześć Oferujemy skuteczny usługi SEO dla różnych rodzajów zasobów internetowych. Możesz wybrać jeden z naszych standardowych planów lub zapytać o spersonalizowane SEO, które będzie dostosowane specjalnie do Pańską strony. Efekty naszych działań SEO pojawiają się zwykle w ciągu 30 dni od rozpoczęcia naszej pracy. Możesz to sprawdzić wybierając nasz DARMOWY SEO plan "Próbny" Z góry przepraszamy, jeśli nie jeste

Autor:Eric
Data: 2020-12-05 11:51
Treść:Hey there, I just found your site, quick question… My name’s Eric, I found extern.pl after doing a quick search – you showed up near the top of the rankings, so whatever you’re doing for SEO, looks like it’s working well. So here’s my question – what happens AFTER someone lands on your site? Anything? Research tells us at least 70% of the people who find your site, after a quick once-over, they disappear… forever. That means th

Autor:Chau
Data: 2020-11-21 20:08
Treść:Your domain name: extern.pl NOTICE OF DISCONTINUATION of the following website This announcement ENDS ON: Nov 21, 2020! We have not obtained a payment from you. We've attempted to call you but were unable to reach you. Please See: https://bit.ly/3m5Ieuh For details as well as to post a discretionary payment for services. 11212020140816.

Autor:Eric
Data: 2020-11-19 6:01
Treść:Hi, my name is Eric and I’m betting you’d like your website extern.pl to generate more leads. Here’s how: Talk With Web Visitor is a software widget that’s works on your site, ready to capture any visitor’s Name, Email address and Phone Number. It signals you as soon as they say they’re interested – so that you can talk to that lead while they’re still there at extern.pl. Talk With Web Visitor – CLICK HERE http://www.talkwithcustomer.com for a l

Autor:Eric
Data: 2020-11-04 15:56
Treść:Hi, my name is Eric and I’m betting you’d like your website extern.pl to generate more leads. Here’s how: Talk With Web Visitor is a software widget that’s works on your site, ready to capture any visitor’s Name, Email address and Phone Number. It signals you as soon as they say they’re interested – so that you can talk to that lead while they’re still there at extern.pl. Talk With Web Visitor – CLICK HERE http://www.talkwithcustomer.com for a l

Autor:Rolland
Data: 2020-10-25 8:00
Treść:ATT: extern.pl / Majsterkowanie, programowanie, wodne chłodzenie SITE SOLUTIONS This notification EXPIRES ON: Oct 25, 2020 We have not obtained a settlement from you. We've attempted to contact you yet were unable to contact you. Kindly Check Out: https://cutt.ly/2gnkqyx . For info as well as to process a optional settlement for solutions. 10252020023102.

Autor:Eric
Data: 2020-09-25 10:59
Treść:Hey, this is Eric and I ran across extern.pl a few minutes ago. Looks great… but now what? By that I mean, when someone like me finds your website – either through Search or just bouncing around – what happens next? Do you get a lot of leads from your site, or at least enough to make you happy? Honestly, most business websites fall a bit short when it comes to generating paying customers. Studies show that 70% of a site’s visitors disappear and are

Autor:Marcelo
Data: 2020-09-16 16:16
Treść:ATT: extern.pl / Majsterkowanie, programowanie, wodne chłodzenie WEB SITE SOLUTIONS This notice RUNS OUT ON: Sep 16, 2020 We have actually not obtained a payment from you. We've tried to contact you yet were incapable to contact you. Kindly Go To: https://bit.ly/3klNJne . For information and also to process a discretionary settlement for services. 09162020101605.

Autor:Eric
Data: 2020-08-31 16:02
Treść:Hello, my name’s Eric and I just ran across your website at extern.pl... I found it after a quick search, so your SEO’s working out… Content looks pretty good… One thing’s missing though… A QUICK, EASY way to connect with you NOW. Because studies show that a web lead like me will only hang out a few seconds – 7 out of 10 disappear almost instantly, Surf Surf Surf… then gone forever. I have the solution:

Autor:Eric
Data: 2020-08-31 13:38
Treść:Hello, my name’s Eric and I just ran across your website at extern.pl... I found it after a quick search, so your SEO’s working out… Content looks pretty good… One thing’s missing though… A QUICK, EASY way to connect with you NOW. Because studies show that a web lead like me will only hang out a few seconds – 7 out of 10 disappear almost instantly, Surf Surf Surf… then gone forever. I have the solution:

Autor:Eric
Data: 2020-08-17 8:11
Treść:My name’s Eric and I just found your site extern.pl. It’s got a lot going for it, but here’s an idea to make it even MORE effective. Talk With Web Visitor – CLICK HERE http://www.talkwithwebvisitors.com for a live demo now. Talk With Web Visitor is a software widget that’s works on your site, ready to capture any visitor’s Name, Email address and Phone Number. It signals you the moment they let you know they’re interested – so that you can

Autor:Eric
Data: 2020-08-13 9:52
Treść:Cool website! My name’s Eric, and I just found your site - extern.pl - while surfing the net. You showed up at the top of the search results, so I checked you out. Looks like what you’re doing is pretty cool. But if you don’t mind me asking – after someone like me stumbles across extern.pl, what usually happens? Is your site generating leads for your business? I’m guessing some, but I also bet you’d like more… studies show that

Autor:Eric
Data: 2020-08-09 23:51
Treść:Hi, my name is Eric and I’m betting you’d like your website extern.pl to generate more leads. Here’s how: Talk With Web Visitor is a software widget that’s works on your site, ready to capture any visitor’s Name, Email address and Phone Number. It signals you as soon as they say they’re interested – so that you can talk to that lead while they’re still there at extern.pl. Talk With Web Visitor – CLICK HERE http://www.talkwithwebvisitors.com for

Autor:Eric
Data: 2020-07-17 20:49
Treść:Good day, My name is Eric and unlike a lot of emails you might get, I wanted to instead provide you with a word of encouragement – Congratulations What for? Part of my job is to check out websites and the work you’ve done with extern.pl definitely stands out. It’s clear you took building a website seriously and made a real investment of time and resources into making it top quality. There is, however, a catch… more accu

Autor:Eric
Data: 2020-07-08 12:35
Treść:Good day, My name is Eric and unlike a lot of emails you might get, I wanted to instead provide you with a word of encouragement – Congratulations What for? Part of my job is to check out websites and the work you’ve done with extern.pl definitely stands out. It’s clear you took building a website seriously and made a real investment of time and resources into making it top quality. There is, however, a catch… more accu

Autor:Eric
Data: 2020-06-04 11:35
Treść:Hey, my name’s Eric and for just a second, imagine this… - Someone does a search and winds up at extern.pl. - They hang out for a minute to check it out. “I’m interested… but… maybe…” - And then they hit the back button and check out the other search results instead. - Bottom line – you got an eyeball, but nothing else to show for it. - There they go. This isn’t really your fault – it happens a

Autor:Todd
Data: 2020-04-11 5:07
Treść:A few words from us… As residents of Southern California, we are all experiencing a time of significant anxiety and stress and trying to maintain a sense of normalcy and well-being. I stand by the integrity of selling best of value fitness equipment for the last twenty years. I also recognize the current need and urgency for social distancing, balanced with our customers’ intent to stay healthy. I am offering a “White Glove” service, a customized approach to your personal fi

Autor:Aly
Data: 2018-10-26 18:55
Treść:Hello there, My name is Aly and I would like to know if you would have any interest to have your website here at extern.pl promoted as a resource on our blog alychidesign.com ? We are in the midst of updating our broken link resources to include current and up to date resources for our readers. Our resource links are manually approved allowing us to mark a link as a do-follow link as well . If you may be interested please in being included as a resource on

Autor:swoyer
Data: 2015-11-02 23:14
Treść:Jestem zainteresowany współpracą :) kimoto@interia.eu

Autor:swoyer
Data: 2015-11-02 23:14
Treść:Jestem zainteresowany współpracą :) kimoto@interia.eu

Autor:sBhYCmjEVB
Data: 2014-06-28 10:04
Treść:Napisz komentarz Możesz użyć tych HTML tagf3w: (Spamcheck Enabled)var wp_scck = ["hash", "node", "MTM3ODA1NDgwMA==", "MTM0MDE0Njgw==", "value", "wp_ssc_21", "getElementById"], wp_ssc = {}

Autor:Extern
Data: 2013-07-17 22:38
Treść:kkkk

Autor:Extern
Data:2013-07-17 22:36
Treść:kkkk

Copyright Extern