reede, 25. jaanuar 2019

Windows Serveri seadistamine Telia X-Tee turvaserverisse SOAP andmete saatmiseks C# veebipäringutega


Kui nüüd on ette tulnud infosüsteemi liidestus X-Teega ja vaja hakata andmeid vahetama läbi Telia XTee turvaserveri tuleb Windows Serveri seadistuses teha serveri poolse ASP.NET koodi jaoks järgmist.

Kontrollida, et serveris oleks TLS 1.2 protokoll lubatud, Telia XTee nõuab TLS 1.2 olemasolu ja ilma selleta ühendust ei tehta. Vastavad TLS 1.2 registrivõtmed DisabledByDefault(0) ja Enabled(1) lisada
HKEY_LOCAL_MACHINE/SYSTEM/CurrentControlSet/Control/SecurityProviders/SCHANNEL/Protocols kataloogi. Sõltuvalt Windows Serveri versionist võib TLS 1.2 olla juba vaikimisi lubatud.



Et kliendi sertifikaadiga autentimine toimiks tuleb võimaldada FIPS, ehk Group Policy alt seadistada
lubatuks Use FIPS compliant algorithms for encryption, hashing, and signing seadistus



Edasi tuleb genereerida avaliku ja privaatvõtme paar ning privaatvõti laadida Windows Serveri sertifikaadihoidlasse Local Computer / Personal

Kuidas genereerida sertifikaate

Selleks, et C# kood sertifikaadiga autentida saaks kas siis .NET rakendusest või SQL SERVER CLR C# meetodiga, peab vastaval kontol, mille all rakendus töötab olema ligipääs sertifikaadihoidlasse laetud privaatvõtmele. Juurdepääsu saab anda ainult Local Computer / Personal hoidlas olevatele privaatvõtmega sertifikaatidele

Local Computer / Personal sertifikaadihoidla avamiseks tuleb C# kasutada StoreName.My parameetrit

X509Store certStore = new X509Store(StoreName.My, StoreLocation.LocalMachine);


Hoidlast räsi järgi otsimiseks X509FindType.FindByThumbprint seadistust. Privaatvõtme serdi räsi saab teada meetodiga X509Certificate2.GetSerialNumberString();

Koodijupp, mis SQL SERVER CLR C# funktsioonina on realiseeritud, selleks, et TLS 1.2 töötaks peab projektis olema kasutatud vähemalt .NET Target Frameworki versioon 4.5 või kõrgem

TLS 1.2 otsene rakendamine ServicePointManager-i kasutades määratakse
ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;

TLS 1.2 ühendust saab luua kas System.Net.Http.HttpClient või System.Net.Security.SslStream klasse kasutades


System.Net.Http.HttpClient näide

    /// <summary>
    ///   Telia XTee turvaserveri kaudu kliendi sertifikaadiga TLS 1.2 protokolli abil SOAP päring
    /// </summary>
    /// <param name="Url">XTee turvaserveri https URL</param>
    /// <param name="envelope">SOAP päringu XML sisu stringina</param>
    /// <param name="serthash">Local Computer / Personal sertifikaadihoidlas oleva sertifikaadi räsi</param>
    /// <returns></returns>
    [Microsoft.SqlServer.Server.SqlFunction]
    public static SqlChars XTeeSOAPRequest(SqlString Url, SqlString envelope, SqlString serthash)
    {
        XmlDocument soapXml = new XmlDocument();
        try
        {
            soapXml.LoadXml((string)envelope);
        }
        catch (SystemException ex)
        {
            throw new SystemException("soapXml.LoadXml ERROR:"+ex.Message+"  SISU:"+(string)envelope);
        }
        string soapResult = "";

     
        ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;
     
        HttpWebRequest webRequest = (HttpWebRequest)WebRequest.Create((string)Url);
     
        string chash = (string)serthash;
        if (!String.IsNullOrEmpty(chash) )
        {
         
            X509Store certStore = new X509Store(StoreName.My, StoreLocation.LocalMachine);
            certStore.Open(OpenFlags.ReadOnly | OpenFlags.OpenExistingOnly);

         
            X509Certificate2Collection serts = certStore.Certificates.Find(X509FindType.FindByThumbprint, chash, true);
            if (serts.Count == 0)
            {
                serts = certStore.Certificates.Find(X509FindType.FindByThumbprint, chash, false);
            }
            if (serts.Count != 1)
            {
                throw new SystemException("Sertifikaati räsiga " + chash + "  ei ole hoidlas Local Computer - Personal");
            }
            X509Certificate2 cert = serts[0];
         
            certStore.Close();
            webRequest.ClientCertificates.Clear();
            webRequest.ClientCertificates.Add(cert);
            if (webRequest.ClientCertificates.Count != 1)
            {
                throw new SystemException("HttpWebRequest sertifikaatide tõrge, vale kogus " + cert.IssuerName.Name);
            }

        }
     
        webRequest.ContentType = "text/xml;charset=\"utf-8\"";
        webRequest.Accept = "text/xml";
        webRequest.Method = "POST";
        webRequest.UserAgent = "KAMPUS";
        using (Stream stream = webRequest.GetRequestStream())
        {
            soapXml.Save(stream);
        }

        using (WebResponse response = webRequest.GetResponse())
        {
            using (StreamReader rd = new StreamReader(response.GetResponseStream()))
            {
                soapResult = rd.ReadToEnd();
             
            }
        }

        SqlChars sd = new SqlChars(((SqlString)soapResult));
        return sd; //siin on vastus, SOAP XML
    }

Rakenduse kontole tuleb anda ligipääs privaatvõtmele, Local Computer / Personal sertifikaadihoidlas tuleb vastava privaatvõtme serdi peale teha hiire paremklõps ja valida



Ning seada Read õigused kontole (konto otsimisel sisesta NT Service/MSSQLSERVER või NT Service\MSSQL)



Mis konto all ASP.NET veebirakendus töötab saab teada C# funktsiooniga
System.Security.Principal.WindowsIdentity.GetCurrent().Name
MS SQL SERVER-i konto Services rakendusest



Veatõrgete jälgimiseks on ASP.NET rakendusel soovitav peale seada System.Diagnostic trace logimine
https://docs.microsoft.com/en-us/previous-versions/dotnet/netframework-4.0/ty48b824(v=vs.100)

Logi veateade The credentials supplied to the package were not recognized  viitab konto õiguste puudumisele privaatvõtmega sertifikaadile ligipääsul

.NET error The request was aborted: Could not create SSL/TLS secure channel viitab privaatvõtmega sertifikaadi puudumisele , FIPS keelamisele või TLS 1.2 mittelubamisele.

Kui ikka segane, pane serveris Wireshark käima


ja jälgi, et nii server teeks Client Certificate küsimise ja klient vastaks ka Certificate vastusega, kui Handshake Protocol Certificates / Certificates Lenght : 0 siis kliendi poolt sertifikaati kaasa ei pandud, kusjuures see kliendi ärakorjamine tehakse Windows Serveri poolt ilma midagi kobisedes ning C# koodi poolt HttpWebRequest.ClientCertificates.Add(cert);

meetod ei ütle kaa midagi paha, et sertifikaati külge panemine on mõttetu, kuna alumised protokollid ei pruugi TLS 1.2 toetada, sertifikaat võetakse küljest Windows Serveri poolt ja veateade mis välja antakse võib olla selline

Server.ClientProxy.SslAuthenticationFailed Client (....) specifies HTTPS but did not supply TLS certificate

Kontrolli FIPS seadistust ja konto õigusi privaatvõtmega sertifikaadile. Kontode puhul tähele panna, et isegi kui ASP.NET rakenduses certStore.Certificates.Find(X509FindType.FindByThumbprint ... leiab privaatvõtmega sertifikaadi ülesse, siis veebipäringu tegemisel ei pruugi see õigus enam toimida vaid vaja on otsest Read õiguse andmist ASP.NET või MS SQL SERVER kontole

TLS 1.2 programmeerimine sõltub tugevalt kasutatavast .NET Framework versioonist, mida kõrgem seda parem. https://docs.microsoft.com/en-us/dotnet/framework/network-programming/tls

Telia Xtee turvaserveri poolne veateade Failed to get signing info for member ... Signer.InternalError: Member .... has no suitable certificates tähendab seda, et sa ei saada õiget privaatvõtme sertifikaati, näiteks saadad avalikku võtit kliendi sertifikaadina.

XTee test turvaserveriga kontrolli, et Sertifitseerimiskeskuse test SK serdid oleks laetud test OCSP’sse (https://demo.sk.ee/upload_cert).


Teine võimalus, kuidas privaatvõtmega kliendi sertifikaati kasutades TLS 1.2 SOAP päringut teha on kasutada System.Net.Security.SslStream klassi, võib anda rohkem veateateid välja

    /// <summary>
    /// Telia XTee turvaserveri kaudu kliendi sertifikaadiga TLS 1.2 protokolli abil SOAP päring TcpClient ja SSLStream kaudu
    /// </summary>
    /// <param name="Url">XTee turvaserveri https URL</param>
    /// <param name="envelope">SOAP päringu XML sisu stringina</param>
    /// <param name="serthash">Local Computer / Personal sertifikaadihoidlas oleva sertifikaadi räsi</param>
    /// <param name="serverIp">XTee turvaserveri IP</param>
    /// <param name="servesertname">XTee turvaserveri sertifikaadi nimi</param>
    /// <returns></returns>
    [Microsoft.SqlServer.Server.SqlFunction]
    public static SqlChars XTeeSOAPSslRequest(SqlString Url, SqlString envelope, SqlString serthash, SqlString serverIp, SqlString servesertname)
    {
        string inputMessage = "";
        string kataloog = (string)tempdir;
        try
        {
            string ServerHostName = (string)serverIp; 
            int ServerPort = 443;
            string ServerCertificateName = (string)servesertname;
            string ymbrik = (string)envelope;
         

            string chash = (string)serthash;

            X509Store certStore = new X509Store(StoreName.My, StoreLocation.LocalMachine);
            certStore.Open(OpenFlags.ReadOnly | OpenFlags.OpenExistingOnly);
            X509Certificate2Collection serts = certStore.Certificates.Find(X509FindType.FindByThumbprint, chash, true);
            if (serts.Count == 0)
            {
                serts = certStore.Certificates.Find(X509FindType.FindByThumbprint, chash, false);
            }
            if (serts.Count != 1)
            {
                throw new SystemException("Sertifikaati räsiga " + chash + "  ei ole hoidlas Local Computer - Personal");
            }
            X509Certificate2 cert = serts[0];
            X509CertificateCollection clientCertificateCollection = new X509CertificateCollection(new X509Certificate[] { serts[0] });
            certStore.Close();
            using (TcpClient client = new TcpClient(ServerHostName, ServerPort))
            {
                using (SslStream sslStream = new SslStream(client.GetStream(), false, App_CertificateValidation))
                {

                    try
                    {
                        sslStream.AuthenticateAsClient(ServerCertificateName, clientCertificateCollection, SslProtocols.Tls12, false);
                    }
                    catch (SystemException ex)
                    {
                        throw new SystemException("SsslStream.AuthenticateAsClient viga(Kontrolli sertifikaadile ligipääsu õigusi) " + ex.Message);
                    }
                    if (sslStream.LocalCertificate == null)
                    {
                        throw new SystemException("SSL LocalCertificate ei ole " + chash);
                    }

                    if (sslStream.RemoteCertificate == null)
                    {
                        throw new SystemException("SSL RemoteCertificate ei ole");
                    }
                    string url = (string)Url;
                    string host = url.Replace("https://", "");
                    host = host.Replace("http://", "");
                    string outputMessage = @"POST " + url + @" HTTP/1.1
Accept: text/xml
Content-Type: text/xml; charset=UTF-8
Content-Length: " + ymbrik.Length + @"
Host: " + host + @"
Connection: Keep-Alive
User-Agent: KAMPUS

" + ymbrik;
                 
                    byte[] outputBuffer = Encoding.UTF8.GetBytes(outputMessage);
                    sslStream.Write(outputBuffer);

                    byte[] inputBuffer = new byte[8096];
                    int inputBytes = 0;
                    while (inputBytes == 0)
                    {
                        inputBytes = sslStream.Read(inputBuffer, 0, inputBuffer.Length);
                    }
                    inputMessage = Encoding.UTF8.GetString(inputBuffer, 0, inputBytes);
                }  //using
            }
        }
        catch (SystemException ex)
        {         
            throw new SystemException(ex.GetType().Name + " " + ex.Message);
        }
        SqlChars sd = new SqlChars(((SqlString)inputMessage));
        return sd;
    }


 private static bool App_CertificateValidation(Object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors)
    {
        if (sslPolicyErrors == SslPolicyErrors.None)
        { return true; }
        if (sslPolicyErrors == SslPolicyErrors.RemoteCertificateChainErrors)
        { return true; } //siin võid ka false tagastada
     
        return false;
    }

Logi veateade AcquireCredentialsHandle() failed with error 0X8009030D. Exception in HttpWebRequest#62571264:: - The request was aborted: Could not create SSL/TLS secure channel. viitab konto õiguste puudumisele privaatvõtmega sertifikaadile, ilmneb see sslStream.AuthenticateAsClient(ServerCertificateName, clientCertificateCollection, SslProtocols.Tls12, false); meetodi juures.

Rakenduse sujuvamaks töötamiseks on tark teha eraldi testfunktsioon System.Net.Security.SslStream AuthenticateAsClient meetodi väljakutsega, kui see annab tõrke on tegu konto ligipääsu õigustega privaatvõtmega sertifikaadile.











teisipäev, 6. november 2018

Kuidas kontrollida, kas IP-aadress pole sattunud spämminimekirja

Üks võimalus kontrolida, mida e-maili server sinu IP-st arvab on teha telnet käsk
"telnet mail.neti.ee 25" ja vaadata, mis vastuseks tuleb

Lõppkasutajale võib teha ekraani peale nupu, kliki seda ja vaata mida kostab

byte[] bytes = new byte[2048];
// Connect the socket to the remote endpoint. Catch any errors.
try
{
                IPHostEntry ipHostInfo = Dns.GetHostEntry(this.LiteralEpostiServer.Text.Trim());
                IPAddress ipAddress = ipHostInfo.AddressList[0];
                Socket sende = new Socket(ipAddress.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
                sende.Connect(ipAddress, Convert.ToInt32(this.LiteralPort.Text.Trim()));
                this.CustomValidator2.ErrorMessage = String.Format("Socket connected to {0}", sende.RemoteEndPoint.ToString());
                this.CustomValidator2.IsValid = false;
                // Encode the data string into a byte array.
                byte[] msg = Encoding.ASCII.GetBytes("This is a test<EOF>");

                // Send the data through the socket.
                int bytesSent = sende.Send(msg);

                // Receive the response from the remote device.
                int bytesRec = sende.Receive(bytes);
                this.CustomValidator3.ErrorMessage = String.Format("Echoed test = {0}", Encoding.ASCII.GetString(bytes, 0, bytesRec));
                this.CustomValidator3.IsValid = false;
                // Release the socket.
                sende.Shutdown(SocketShutdown.Both);
                sende.Close();
}
catch (ArgumentNullException ane)
{
                this.CustomValidator1.ErrorMessage = "ArgumentNullException: " + ane.ToString();
                this.CustomValidator1.IsValid = false;
}
catch (SocketException se)
{
                //Console.WriteLine("SocketException : {0}", se.ToString());
                this.CustomValidator1.ErrorMessage = "SocketException: " + se.ToString();
                this.CustomValidator1.IsValid = false;
}
catch (SystemException ex)
{
                this.CustomValidator1.ErrorMessage = ex.Message;
                this.CustomValidator1.IsValid = false;
}

Kui vaja lugeda SQL DBMail serveri profiilist andmed, siis selleks järgmine salvestatud protseduur, mis kasutab lõpuks msdb.dbo.sysmail_help_account_sp protseduuri

ALTER PROCEDURE [dbo].[E_POSTI_SERVER_PROFIILIST]
WITH EXECUTE AS 'DBO'
AS
BEGIN
SET NOCOUNT ON
DECLARE @mail_profiil VARCHAR(100), @accountID INT
SET @mail_profiil = ( SELECT MAX(string) FROM DBO.DB_DEFAULT_PARAMS_S('MAIL_SERVER_PROFILE') WHERE erro=0 )
DECLARE @profiil TABLE (profile_id int, profile_name sysname, account_id int, account_name sysname, sequence_number int)
INSERT INTO @profiil EXEC msdb.dbo.sysmail_help_profileaccount_sp @profile_name=@mail_profiil
SET @accountID=(SELECT TOP 1 account_id FROM @profiil)
--SELECT * FROM @profiil
EXEC msdb.dbo.sysmail_help_account_sp  @account_id = @accountID
END


kolmapäev, 13. juuni 2018

Database Maili viga The EXECUTE permission was denied on the object 'sp_send_dbmail', database 'msdb', schema 'dbo'

Andmebaasis on protseduur, mis kutsub välja msdb.dbo.sp_send_dbmail protseduuri e-posti saatmiseks.

Kõik on nagu õieti, tehtud on kontod ja profiild ning andmebaasi loginid on lisatud msdb andmebaasi DataBaseMailUser rolli

aga e-post saatmisel annab vea The EXECUTE permission was denied on the object 'sp_send_dbmail', database 'msdb', schema 'dbo'

Lahendus on see, et andmebaas tuleb seada  TRUSTWORTHY režiimi, ehk

ALTER DATABASE andmebaasi_nimi SET TRUSTWORTHY ON

Saadetud e-postide olekut saab kontrollida päringuga
select * from msdb.dbo.sysmail_allitems

Kui on kasuatusel protseduurides EXECUTE AS konstruktsioon siis võib vaja minna muuta ka andmebaasi omanikku, näiteks

EXEC sp_changedbowner 'sa'

ALTER AUTHORIZATION ON DATABASE::[andmebaas] TO [sa]


pühapäev, 8. aprill 2018

Andmeseti hoidmine peale päringut


Mõnikord siis baasi päring kestab kaua ja midagi pole teha, päring käib sekundeid ja kui soov pärast kasutada DataGridi tulpade sorteerimist siis pole aega oodata, teist korda baasist päringut teha ei ole mõtekas.

Lahendab olukorra nii, et päringustsaadud andmeseti paneb kas Sessioni või ViewState-e ja järgmistel operatsioonidel, näiteks tulpade sorteerimine kasutab salvestatud andmeseti tulemusi.

private DataTable dt
        {
            set { this.ViewState["dt"] = value; }
            get { return (DataTable)this.ViewState["dt"]; }
        }

Andmete lugemine DataSeti SqlDataReader-ist

SqlDataReader red = komm.ExecuteReader();
this.dt = new DataTable();
this.dt.Load(red);
this.GridViewVolad.DataSource = this.dt;
this.GridViewVolad.DataBind();

SqlDataSource-st, peale parameetritele väärtuste andmist

DataSourceSelectArguments args = new DataSourceSelectArguments();
DataView view = (DataView)SqlDataSourceYyrnikud.Select(args);
this.dt = view.ToTable();
this.GridView1.DataSource = this.dt;
this.GridView1.DataBind();

GridView sorteerimiseks on vajalikud meeles pidada sorteerimisjärjestus ja sortimisavaldus

        private string SortExpression
        {
            set { this.ViewState["SortExpression"] = value; }
            get
            {
                if (this.ViewState["SortExpression"] == null)
                {
                    this.ViewState["SortExpression"] = "ACCOUNT_ID";  //vaikimisi järjestus
                }
                return this.ViewState["SortExpression"].ToString();
            }
        }

        private string SortDirection
        {
            set { this.ViewState["SortDirection"] = value; }
            get
            {
                if (this.ViewState["SortDirection"] == null)
                {
                    this.ViewState["SortDirection"] = "";
                }
                return this.ViewState["SortDirection"].ToString();
            }
        }


        protected string GetSortingDirection()
        {
            if (this.SortDirection == "")
            {
                this.SortDirection = "DESC";
            }
            else if (this.SortDirection == "ASC")
            {
                this.SortDirection = "DESC";
            }
            else
                this.SortDirection = "ASC";

            return this.SortDirection;
        }

GridView-le aga tuleb lisada Sorting meetod

        protected void GridViewVolad_Sorting(object sender, GridViewSortEventArgs e)
        {
            DataView view = this.dt.DefaultView;
            view.Sort = String.Format("{0} {1}", e.SortExpression, GetSortingDirection());
            this.SortExpression = e.SortExpression;
            this.GridViewVolad.DataSource = dt;
            this.GridViewVolad.DataBind();
        }

Kui nüüd GridView tulpasid sorteerida võetakse andmed this.dt DataTable-st ja baasist küsima ei minda.

pühapäev, 17. detsember 2017

SQL SERVER CLR serialization assembly

Kui juhtub, et SQL SERVER-i veebiteenuse poole pöörduv CLR hakkab pilduma viga

Cannot load dynamically generated serialization assembly. In some hosting environments assembly load functionality is restricted, consider using pre-generated serializer. Please see inner exception for more information.  System.IO.FileLoadException: LoadFrom(), LoadFile(), Load(byte[]) and LoadModule() have been disabled by the host

Siis soovitatakse luua selle DLL jaoks ka Serialization assembly. Selleks tuleb antud projekt
VisualStudio 2015 kõigepealt signeerida



Serializer dll tegemiseks kasutab sgen.exe käsurida

sgen /force /compiler:/keyfile:c:\Campus\KampusPdfInvoice\KampusPFDInvoice.snk KampusPdfInvoice.dll

tulemusena tekib KampusPdfInvoice.XmlSerializers.dll fail, mis
tuleb andmebaasi sisse lugeda peale originaal DLL sisselugemist, ehk kõigepealt

CREATE ASSEMBLY [KampusPdfInvoice] ..

ja siis

CREATE ASSEMBLY [KampusPdfInvoice.XmlSerializers] ...




neljapäev, 23. veebruar 2017

Ekraanikattega akna sulgemine ESC klahvivajutusega

Üllataval kombel ei leidnud selleks lihtsat lahendust, tuleb Javascriptiga jännata

Visuaalselt näeb välja nii, et klikid kugugi ja avaneb ekraaninkattega aken.



Kasutame ModalPopupExtender-it mille ees näitab UserControli sisu
<asp:Label runat="server" ID="HForModal" style="display: none" />
<asp:Panel runat="server" ID="P1" ScrollBars="Auto" Wrap="true"  Width="80%" CssClass="modalPopup">
    <asp:Button ID="ButtonModalClose" ClientIDMode="Static" runat="server" CausesValidation="False" CssClass="actionButton" Text="<%$ Resources:resource, ButtonClose %>" />&nbsp;
    <uc3:BoxNeibhours ID="BoxNeibhours1" runat="server" kuupaevNahtav="false"/>
</asp:Panel>
<ajaxToolkit:ModalPopupExtender runat="server" ID="ModalPopupExtender1" X="120" BackgroundCssClass="moduleBackground" TargetControlID="HForModal" OkControlID="ButtonModalClose"
        CancelControlID="ButtonModalClose" PopupControlID="P1" BehaviorID="HForModal">
</ajaxToolkit:ModalPopupExtender> 

Kui nüüd see ekraanikattega aken avaneb, tahaks teda ESC klahviga sulgeda, aga ei kao ta ühtegi

CancelControlID ei mõju ESC klahvile, ehk JavaSript appi


 if (!Page.ClientScript.IsClientScriptIncludeRegistered("onKeyEsc"))
            {
                string url = ResolveClientUrl("~/Scripts/SulgeModal.js");
                Page.ClientScript.RegisterClientScriptInclude("onKeyEsc", url);
            }



JavaSript ise SulgeModal.js selline

function pageLoad(sender, args) {
        if (!args.get_isPartialLoad()) {
            //  adding handler to the document's keydown event
            $addHandler(document, "keydown", onKeyDown);
        }
    }
function onKeyDown(e) {
    if (e && e.keyCode == Sys.UI.Key.esc) {
        // if the key pressed is the escape key, then close the dialog
        //alert("close");
        var bname = document.getElementById("ButtonModalClose");  //ClientIDMode="Static"      
        if (bname !== null)
        {
            bname.click();
        }
        //PageMethods.SulgeAken(); //ei tööta
    }
}

Tähele tuleb panna seda kohta JavaScriptis

document.getElementById("ButtonModalClose")

vastav Button tuleb ClientIDMode="Static" defineerida

asp:Button ID="ButtonModalClose" ClientIDMode="Static"

kolmapäev, 21. detsember 2016

Märkeruudu ärakaotamine CheckBoxListi grupeerimisel

ASP.NET CheckBoxList komponendil mõnikord vaja nimekirja grupeerida erinevatel põhjustel


Kogu trikk põhineb CSS3 kasutamisel, kuna see võib tekitada segadusi projekti piirides, siis mõtekas kasutada "inline CSS", näiteks vajaliku userControli sees

<style>
    #CheckBoxListOtsinguMajad input:disabled {
        display: none;
    }
</style>
reegli mõju piirame CheckBoxListOtsinguMajad komponendi nende osadega, mille input:disabled, ehk märkeruuduga, sättida tuleb ka  ClientIDMode="Static" , et ASP.NET komponendi ID-d ei muudaks ja CSS3 reegel komponendi tuvastaks

asp:CheckBoxList ID="CheckBoxListOtsinguMajad" ClientIDMode="Static"
Kui nüüd CheckBoxListi ListItem seada Enabled=False, siis vastavale reale CSS reegel rakendub ja kuna display: none; siis märkeruut kaob ära


protected void CheckBoxListOtsinguMajad_PreRender(object sender, EventArgs e)
    {
        foreach (ListItem it in this.CheckBoxListOtsinguMajad.Items)
        {
            if (it.Value.Equals("0")) //grupeeringu tunnus
            {
                it.Enabled = false;
                it.Selected = false;
                it.Attributes.Add("style", "font-size:14px;height:16px;font-weight:bold;");
            }
        }
    }

 Grupeeringu rida aga võiks teistest tugevamalt eristada, täiendame stiili
 it.Attributes.Add("style", "font-size:14px;height:16px;font-weight:bold;");