HOW TO

Pytanie – rozwiązanie [1] WPF – okno bez obramowania

Postanowiłem zacząć umieszczać na blogu krótkie posty na zasadzie pytania i rozwiązania, które napotykałem podczas pisania różnych projektów.

Na początek coś prostego, ale umknęło mi to z pamięci i musiałem przeszukać właściwości okna, bo coś kojarzyłem, ale nie pamiętałem nazwy :) Odpowiedź na pytanie “Jak usunąć obramowanie okna w WPF?”  jest następująca:

We właściwościach okna ustaw WindowState=None

Dlaczego takie coś robić? Przykładem może być aplikacja, która ma wyglądać inaczej niż standardowe okienko. Zamiast tła ma ustawiony obrazek z przezroczystością. Można dzięki temu uzyskać bardzo ciekawy design, jednakże bez ustawienia WindowState=None cały efekt będzie zepsuty przez domyślny pasek okna i jego obramowanie.


XNA + Modele 3D = Blender

Dziś postanowiłem podzielić się swoimi początkami jeżeli chodzi o platformę XNA oraz wykorzystanie modeli 3D. Na rynku mamy wiele programów do modelowania 3D, natomiast jednym z popularniejszych, a dodatkowo bezpłatnych programów jest Blender. Aktualnia stabilna wersja to 2.49b i to właśnie ona posłuży nam do przygotowania i wyeksportowania modelu.

Wersja podstawowa

Uruchamiamy zatem Blendera (wymagany jest w tym zainstalowany Python 2.6). Widzimy domyślny układ okna, na którym znajduje się sześcian, lampa i kamera. Do XNA będziemy eksportować pojedyncze modele, zatem pozbywamy się kamery i lampy, lub usuwamy wszystko (klawisz A, następnie Del) i dodajemy sześcian na nowo (Spacja -> Add -> Mesh -> Cube). Co do samego modelu pozostawiam wam pole do popisu. Ja w tym momencie pozostanę przy sześcianie :)

XNA potrafi korzystać z modeli zapisanych między innymi w formacie FBX. Blender w ostatniej stabilnej wersji wspiera eksport do takiego właśnie formatu. Jezeli korzystacie z jakiejś starczej, to również nie ma problemu, gdyż istnieją gotowe skrypty, które umożliwiają eksport do takiego właśnie formatu.
Klikamy zatem Menu File -> Export -> Autodesk FBX (.fbx). W tym momencie nie korzystamy z animacji, zatem możemy odznaczyć pole Enable Animation, a następnie zapisujemy plik na dysku.

Aby móc skorzystać z tak wyeksportowanego modelu w XNA najpierw musimy stworzyć projekt. W tym celu skorzystamy z szablonu Windows Game. Równie dobrze można skorzystać z projektu dla Windows Phone 7. Jedyna różnica jaka tu będzie, to inne współrzędne dla ustawień kamery, ale ich dopasowanie nie jest problemem.


W przeciwieństwie do wcześniejszych wersji XNA mamy tu do czynienia z oddzielnym projektem.


Dla zachowania porządku dodajemy sobie katalog Models, do którego następnie dodajemy wyeksportowany przed chwilą obiekt w formacie fbx.
Teraz pora na kawałek kodu. W pliku Game1.cs dodajemy zmienne:

1
2
3
private Model _model;
float aspectRatio;
Vector3 cameraPosition = new Vector3(10.0f, 10.0f, 10.0f);

W metodzie LoadContent musimy zainicjować ww zmienne w taki oto sposób:

1
2
_model = Content.Load<Model>("Models/sampleModel");
aspectRatio = graphics.GraphicsDevice.Viewport.AspectRatio;

Teraz przechodzimy do metody Draw, która jest odpowiedzialna za idświeżanie widoku. Naszym zadaniem jest narysowanie każdej powierzni po uwczesnym nadaniu jej odpowiednich wartości oświetlenia i projekcji. Gotowy kod wygląda następująco.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
protected override void Draw(GameTime gameTime)
{
GraphicsDevice.Clear(Color.CornflowerBlue);
// Draw the model.
foreach (ModelMesh mesh in _model.Meshes)
{
foreach (BasicEffect effect in mesh.Effects)
{
effect.EnableDefaultLighting();
effect.View = Matrix.CreateLookAt(cameraPosition,
Vector3.Zero, Vector3.Up);
effect.Projection = Matrix.CreatePerspectiveFieldOfView(
MathHelper.ToRadians(20.0f), aspectRatio,
1.0f, 50.0f);
}
 
mesh.Draw();
}
 
base.Draw(gameTime);
}

W tym momencie jesteśmy gotowi do uruchomienia projektu, którego efekt powinien prezentować się jak na poniższym screenie:

Scena jest trochę statyczna, więc możemy jeszcze dodać rotację. W tym celu tworzymy dodatkową zmienną, która będzie przechowywała aktualny kąt obrotu (float modelRotation) oraz uaktualniamy ją w metodzie Update:

1
modelRotation += (float)gameTime.ElapsedGameTime.TotalMilliseconds * MathHelper.ToRadians(0.1f);

Teraz w metodzie Draw dodajemy kod odpowiedzialny za zaaplikowanie odpowiedniego obrotu (na przykład względem osi OY). Pamiętajmy, że XNA to świat macierzy, dlatego korzystamy z takiego kawałka kodu:

1
effect.World = Matrix.CreateRotationY(modelRotation);

Jeżeli chcemy zastosować więcej obrotów musimy przemnożyć przez siebie odpowiednie macierze. Teraz po uruchomieniu projektu nasz sześcian ożył :)

Teksturowanie

Jednolite obiekty to nie jest szczyt marzeń, dlatego przydałoby się wykorzystać jakąś teksturę. Tutaj sprawy się nieznacznie komplikują. Tak jak w kwesti modelowania i eksportowania modeli ze standardowymi materiałami nie ma tu większej magii, tak w przypadku wykorzystywania tekstur są pewne (czasem spore) różnice w każdej wersji Blendera. W internecie można znaleźć różne posty, jednakże nie wpadł mi w ręce żaden odpowiadający tej właśnie wersji. Zatem metodą prób i błędów z małą pomocą dokumentacji przedstawiam kolejne ktoki, jakie musimy podjąć aby przygotować taki model.

  • Tworzymy w blenderze nowy projekt.
  • Dodajemy sześcian
  • Dzielimy ekran na dwie części
  • Przenosimy się do trybu Edit
  • W zakładce Editing (F9) wybieramy UV texture -> New
  • W jednej z części zostawiamy widok 3D, a w drugiej przełączamy się na UV/Image Editor
  • Przełączamy się w tryb edycji powierzchni i Wybieramy jedną z nich.
  • Wybieramy plik graficzny, który ma być teksturą danej powierzchni
  • Przełączamy w tryb Textured, dzięki któremu mamy podgląd tego co zrobiliśmy

Analogiczne operacje wykonujemy dla kolejnych powierzchni (w tym przypadku dla ścian sześcianu). Jeżeli zaznaczymy kilka wówczas załadowanie pliku graficznego spowoduje, że zostanie on wczytany dla wszystkich ścian.

Teraz czas zapisać pracę i ponownie wyeksportować model do formatu fbx, który następnie przenosimy do projektu. Jest jednak jedna rzecz, którą musimy zmienić. W pliku modelu mamy zapisane informacje o teksturach w postaci relatywnych ścieżek. Musimy je pozmieniać na odpowiednie wartości. Jednocześnie w projekcie tworzymy katalog Textures, do którego przenosimy wykorzystane przez nas tekstury. Otwieramy plik FBX przy pomocy edytora tekstowego (notatnik, lub dwuklik w Visual Studio) i wyszukujemy wystąpień RelativeFilename a następnie je uaktualniamy:
RelativeFilename: “..\Textures\sampleTexture.png” oraz RelativeFilename: “..\Textures\sampleTexture2.png” Teraz po wykorzystaniu nowego modelu możemy się cieszyć sześcianem z teksturami.

To koniec. Analogicznie postępujemy z bardziej złożonymi obiektami i teksturami, dzięki czemu dużo łatwiej możemy tworzyć niesamowitą grafikę na platformie XNA przy użyciu intuicyjnego programu do modelowania jakim jest Blender.

Projekt do pobrania.


WP7 – Silverlight + XNA != InvalidOperationException

Dziś po małej przerwie mała podpowiedź dla osób zajmujących się Windows Phone 7.

Jak wiadomo (lub też nie :) ) platformy, przy pomocy których możemy pisać własne aplikacje na WP7 to Silverlight 4 Windows Phone (nie piszę samego Silverlight, bo są tu pewne różnice w stosunku do pełnej wersji) oraz XNA 4.0. Nie oznacza to jednak, że musimy zamykać się tylko w jednym ze światów. Istnieje możliwość korzystania w jednym projekcie z przestrzeni nazw, klas i funkcjonalności dostępnych w obu tych technologiach. Zapewne najczęstsze “wymieszanie” technologii następuje gdy tworzymy w Silverlight. Dlaczego? Między innymi dlatego, że XNA oferuje nam wiele funkcjonalności multimedialnych takich jak odtwarzanie dźwięków, nagrywanie przy pomocy mikrofonu, MediaPlayera, ale również udostępnia rozpoznawanie gestów przy pomocy TouchPanel (tak – wiem, że Silverlight mamy Silverlight Control Toolkit i GestureListener, ale tak naprawdę jest on nakładką na TouchPanel z XNA :) ).

Tak czy inaczej, może okazać się, że w momencie gdy dodamy do projektu referencję do klas z XNA i będziemy korzystać z zawartych tam obiektów może się okazać, że otrzymujemy błąd widoczny na screenie poniżej, czyli InvalidOperationException, FrameworkDispatcher.Update has not been called.

Dispatcher

Oznacza on tyle, że korzystamy z elementów, które wymagają wywołania metody “odświeżenia stanu XNA” . Aby to zrobić wystarczy użyć takiego oto fragmentu kodu w konstruktorze strony SL:

1
2
3
4
DispatcherTimer dt = new DispatcherTimer();
dt.Interval = TimeSpan.FromMilliseconds(33);
dt.Tick += delegate { try { FrameworkDispatcher.Update(); } catch { } };
dt.Start();

W tym momencie przy uruchomieniu aplikacji błąd ten się nie pojawi. Warto jeszcze zauważyć, że nie ma sensu ustawiać częstszego odświeżania (czyli interwału dla DispatcherTimer), gdyż platforma Windows Phone 7 wspiera maksymalnie 30 klatek na sekundę (co daje odświeżenie do 33 milisekundy), w przeciwieństwie do pełnego XNA, które wspiara standardowo 60 fps.


Prawda o SqlCommand.Parameters

Czy zastanawialiście się kiedyś jak dodawać parametry do SqlCommand? Klasa ta zawiera pole Parameters typy SqlParameterCollection, na którym możemy wykonać między innymi metodę AddWithValue(string, object). W internecie jak i w dokumentacji w nazwach parametrów na początku jest zawsze użyty znak “@”.

Przy okazji jednego z projektów musiałem odpowiedzieć sobie na pytanie: Czy muszę zadbać o “@” przy nazwie parametru? Nie zastanawiając się długo postanowiłem, zgodnie duchem empiryzmu, sprawdzić co się stanie w poszczególnych sytuacjach.

Baza danych.

Dla testów mała baza danych z jedną procedurą pobierającą dwa parametry:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
CREATE DATABASE SqlCmdParametersTest
USE [SqlCmdParametersTest]
CREATE PROCEDURE [dbo].[spTest]
	@Param1 INT,
	@Param2 INT
AS
BEGIN
	SET NOCOUNT ON;
	SELECT
 		@Param1 Col1,
		@Param2 Col2END
GO
-- TEST
EXEC [dbo].[spTest] 1, 2
EXEC [dbo].[spTest] @Param1=1, @Param2=2
EXEC [dbo].[spTest] @Param2=1, @Param1=2

Aplikacja testowa

Teraz nie pozostaje nic innego jak przygotować małą aplikację konsolową, która skorzysta z tej procedury. Wiadomo, że podstawowym sposobem dodawania parametrów jest użycie “@” na początku nazwy, zatem będzie to pierwszy sposób użycia. Jako drugi przypadek weźmiemy parametr bez tego znaku. Czy to wszystkie możliwości? Nie – można to przecież jeszcze połączyć i użyć raz z “@” a raz bez – ot tak, żeby było trudniej. Zatem do dzieła:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
using System;
using System.Data;
using System.Data.SqlClient;
 
namespace SqlCmdParametersTest
{
    class Program
    {
        static void Main(string[] args)
        {
            using (SqlConnection sqlConn = new SqlConnection(@"Data Source=localhost;Initial Catalog=SqlCmdParametersTest;Integrated Security=SSPI;Persist Security Info=true"))
            {
                sqlConn.Open();
                using (SqlCommand sqlCmd = new SqlCommand())
                {
                    sqlCmd.CommandType = CommandType.StoredProcedure;
                    sqlCmd.Connection = sqlConn;
                    sqlCmd.CommandText = "[dbo].[spTest]";
 
                    //call with @
                    Console.WriteLine("With @");
                    sqlCmd.Parameters.Clear();
                    sqlCmd.Parameters.AddWithValue("@Param1", 1);
                    sqlCmd.Parameters.AddWithValue("@Param2", 2);
                    using (SqlDataReader reader = sqlCmd.ExecuteReader())
                    {
                        ReadResult(reader);
                    }
 
                    //call without @
                    Console.WriteLine("Without @");
                    sqlCmd.Parameters.Clear();
                    sqlCmd.Parameters.AddWithValue("Param1", 1);
                    sqlCmd.Parameters.AddWithValue("Param2", 2);
                    using (SqlDataReader reader = sqlCmd.ExecuteReader())
                    {
                        ReadResult(reader);
                    }
 
                    //call mixed
                    Console.WriteLine("Mixed");
                    sqlCmd.Parameters.Clear();
                    sqlCmd.Parameters.AddWithValue("@Param1", 1);
                    sqlCmd.Parameters.AddWithValue("Param2", 2);
                    using (SqlDataReader reader = sqlCmd.ExecuteReader())
                    {
                        ReadResult(reader);
                    }
                }
            }
 
            Console.ReadLine();
        }
 
        private static void ReadResult(SqlDataReader reader)
        {
            if (reader.Read())
            {
                Console.WriteLine("Result:");
                Console.WriteLine("{0} {1}", reader["Col1"], reader["Col2"]);
            }
            else
            {
                Console.WriteLine("Result is empty");
            }
        }
    }
}

Wynik dziąłania aplikacji widać na rysunku poniżej:

SqlCmdParametersTestResult

Jak widać wynik jest zgodny z oczekiwaniami – wszystko działa poprawnie, nawet “mix”. Warto jeszcze sprawdzić co dociera do SQL Servera, bo może to on jest tą “sprytną stroną”… Po uruchomieniu SQL Profilera widzimy, że jednak krok ku wygodzie a przede wszystkim swobodzie jest po stronie .NET Framework.

SqlCmdParametersTestSqlProfiler

Wniosek

Z SqlCommand.Parameters można korzystać swobodnie i bez martwienia się o “@” lub jej brak.

Paczka do pobrania


ASP.NET Button i jQuery dialog

jQuery jak zapewne każdy wie jest bardzo przydatnym i użytecznym narzędziem. jQuery UI to zestaw wtyczek (możemy je nazwać kontrolkami), które pozwalają na szybkie “upiększenie” naszej strony. Okazuje się jednak, że bywają pewne kolizje na drodze jQuery UI i ASP.NET. Przykładem może tu być wykorzystanie pluginu “dialog” dla kontenerów (np. div) zawierających przyciski ASP:Button.

<div id="dialogDiv">
   Dialog content
   <asp:Button id="btnDoSomething" runat="server" Text="Do something" onclick="btnDoSomething_click"/>
</div>

Okazuje się, że gdy chcemy wykonać $(“#dialogDiv”).dialog(‘open’); to niestety przycisk, który się znajduje w wyskakującym okienku przestaje działać. Aby tego uniknąć musimy dodać wykorzystać poniższy kawałek kodu.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
function showjqdialog(){
   jQuery(document).ready(function() {
      var dlg = $("#dialogDiv").dialog({
            modal: true,
            overlay: {
                backgroundColor: '#000',
                opacity: 0.5
             },
           width:500
      });
      dlg.parent().appendTo(jQuery("form:first"));
      dlg.dialog('open');
   });
}

Najważniejszy fragment to ustawienie rodzica naszego okienka na formularz dokumentu tak, aby mógł on przejąć akcję przycisku. Chodzi tu oczywiście o fragment:

1
dlg.parent().appendTo(jQuery("form:first"));

Dodatkowo pokazanie okna dialogowego najlepiej jest wykonać w momencie gdy dokument jest już w pełni załadowany. Chodzi tu oczywiście o fragment jQuery(document).ready(function() {…});

Dla dopełnienia informacji na temat jQuery UI dialog i działania z ASP.NET kilka słów na temat jak go uruchomić warunkowo z code behind. Otóż w metodzie obsługującej kliknięcie przycisku dodajemy:

1
2
3
if(showDialogCondition){
   ClientScript.RegisterStartupScript(Page.GetType(), "dialogscript", "<script type="\"text/javascript\""><!--mce:0--></script>");
}

Dzięki temu podczas ładowania strony zostanie doładowany kawałek skryptu odpowiedzialny za pokazanie okna dialogowego. Pamiętając o tym, że czekamy w funkcji showjqdialog() na załadowanie dokumentu mamy pewność o poprawnym działaniu kodu.


WCF i Handler “svc-Integrated” has a bad module “ManagedPipelineHandler” in its module list

Czas na trzeci już i ostatni błąd na który natrafiłem przy próbach wdrożenia serwisu WCF na serwer IIS. Brzmi on następująco:

Handler “svc-Integrated” has a bad module “ManagedPipelineHandler” in its module list Handler "svc-Integrated" has a bad module "ManagedPipelineHandler" in its module list

Podobnie jak w przypadku wcześniejszego błędu “The page you are requesting cannot be served because of the extension configuration” wystarczy wykorzystać narzędzie dostarczone z Visual Studio i dostępne w Visual Studio Command Prompt. Jest nim aspnet_regiis.exe. Podobnie jak poprzednie uruchamiamy je z parametrem -i. Zatem całość prezentuje się następująco:

aspnet_regiis.exe -i

aspnet_regiis.exe -i

Oczywiście należy pamiętać o uruchomieniu Visual Studio Command Prompt w kontekście administratora, gdyż w przeciwnym wypadku operacja ta się nie powiedzie.

Przyczyną tych błędów jest fakt, że zanim zainstalowałem serwer IIS miałem już zainstalowane Visual Studio. Jeżeli rozpatrujemy odwrotną kolejność instalacji, wowczas VS samodzielnie rejestruje i dodaje do systemu odpowiednie komponenty. Nie mniej jednak jeżeli podobnie jak ja zamienicie kolejność, wówczas te trzy posty mogą wam się przydać.

Dla przypomnienia wcześniejsze potyczki z błędami:

WCF i The page you are requesting cannot be served because of the extension configuration
WCF i This assembly is built by a runtime newer than the currently loaded runtime and cannot be loaded.


WCF i This assembly is built by a runtime newer than the currently loaded runtime and cannot be loaded.

Drugi problem, na który natrafiłem przy instalacji serwisu WCF na IIS brzmiał

This assembly is built by a runtime newer than the currently loaded runtime and cannot be loaded.

Co w tej sytuacji zrobić. Okazuje się, że tym razem sprawa jest dużo prostsza niż w przypadku poprzedniego błędu. Tym razem wystarczy nieco namieszać w ustawieniach pól aplikacji serwera IIS. Problem polega na tym, że póla, w kontekście której uruchamiana jest usługa, działa w oparciu o niewłaściwą wersję .NET Framework. W moim przypadku ustawiona jest wersja 2.0, natomiast udostępniana przeze mnie usługa wymaga wersji 4.0. W związku z tym najwygodniej jest utworzyć w panelu zarządzania pólami aplikacji nową, na przykład o nazwie .NET 4 i w ustawieniach zaawansowanych wybrać odpowiednią wersję .NET Framework

application pool settings

Kolejnym krokiem jest wybranie dla naszej aplikacji nowo utworzonej póli z właściwą wersją Frameworka i gotowe.


WCF i The page you are requesting cannot be served because of the extension configuration

W momencie gdy chcemy uruchomić na serwerze IIS usługę stworzoną przy pomocy technologii WCF pojawia się nieoczekiwany błąd:

The page you are requesting cannot be served because of the extension configuration. If the page is a script, add a handler. If the file should be downloaded, add a MIME map.

The page you are requesting cannot be served because of the extension configuration

Dzieje się tak w przypadku odwołania do plików *.SVC, które są odpowiedzialne za udostępnianie kontraktów usług na zewnątrz.

W jaki sposób go rozwiązać? Otoż pierwsze co należy zrobić, to uruchomienie Visual Studio Command Prompt w trybie administratora. Następnie należy udać się do katalogu:

%windir%\Microsoft.NET\Framework\v3.0\Windows Communication Foundation

W tymże katalogu znajduje się narzędzie ServiceModelReg.exe, które należy uruchomić z parametrem -i. W wyniku otrzymujemy informację o zainstalowaniu poszczególnych komponentów niezbędnych do uruchomienia usług WCF przy pomocy serwera IIS. Wynik działania polecenia widoczny jest poniżej.

 ServiceModelReg.exe -i result


WCF i enum – udostępnianie typu wyliczeniowego

Często w pracy z Windows Communication Foundation chcemy udostepnić w kontrakcie danych naszej usługi pewien typ wyliczeniowy. Aby to zrobić wykorzystujemy atrybut [EnumMember], który umieszczamy nad każdą z wartości tego typu. Posłużę się przykładem, który przygotowałem do prezentacji SOA + .NET = WCF, którą prowadziłem na ostatnim spotkaniu TGD.NET.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
using System.Runtime.Serialization;
 
namespace CarRentalServices.DataTransferObjects
{
    [DataContract(Namespace="CarRentalServices.Types")]
    public enum CarMake
    {
        [EnumMember]
        Ford,
 
        [EnumMember]
        Opel,
 
        [EnumMember]
        Peugeot,
 
        [EnumMember]
        Dodge,
 
        [EnumMember]
        Chrysler
    }
}

Podczas prezentacji zastanawialiśmy się co się stanie, jeżeli w typie wyliczeniowym nie użyjemy tego atrybutu? Niestety nie było na to czasu podczas spotkania, jednakże postanowiłem to sprawdzić. Otóż okazuje się, że klient zinterpretuje go jako string, ale próba pobrania obiektu, który zawiera w sobie pole tego typu wygeneruje błąd nieoczekiwanego zakmnięcia połączenia (“The underlying connection was closed: The connection was closed unexpectedly.”). Wystąpi on nawet jeżeli po stronie klienta nie będziemy korzystać z tego typu – nie ustawimy wartości pól o rozpatrywanym typie wyliczeniowym. Dlaczego? Ponieważ typ wyliczeniowy jest inicjowany poprzez pierwszą wartość z listy – w tym przypadku będzie to Ford. Bład powstaje natomiast przez to, że bez atrybutu nad wartościami typu wyliczeniowego zmienia się definicja kontraktu i jednocześnie powstają nieścisłości po stronie klienta podczas desierializacji wysłanej przez serwis odpowiedzi. Jeżeli klient nie potrafi zinterpretować odpowiedzi wówczas zamyka połączenie.

Błąd ten nie powstaje jeżeli atrybutem [EnumMember] oznaczymy tylko te wartości, które mogą być przesłane do klienta, których użyjemy w komunikacji. Wszystkie pozostałe mogą pozostać bez niego.

Wnioski:

1. Typ wyliczeniowy bez wartości oznaczonych atrybutem [EnumMember] powoduje błędy przy korzystaniu z uslugi.
2. Błąd, który nic nie mówi może oznaczać błędną definicję kontraktu danych.


SQL Management Studio Specify Values for Template Parameters

Korzystając z Microsoft SQL Server Management Studio czasem zdarza nam się korzystać z szablonów. Zdarza się to gdy dodajemy nową procedurę, funkcję, tryger i inne, lub korzystamy z eksploratora szablonów (Ctrl+Alt+T, View -> Template Explorer). Szablony posiadają pewne parametry rozumiane przez SSMS. Aby z nich skorzystać używamy skrótu Ctrl-Shift-M. Wówczas pojawia nam się okno 9przykład dla procedury składowanej):

Specify Stored Procedure Parameters

Co jednak zrobić, jeżeli to nie działa?

Należy w menu wybrać Tools > Options, następnie Keyboard jak na poniższym obrazku:

Tools_Options_Keyboard_KeyboardScheme

Tam w opcji Keyboard Scheme wybrać najpierw SQL Server 2000, kliknąć OK i wykonać wszystko ponownie tym razem wracając ustawienie na Standard. Tym razem w oknie szablonu skrót Ctrl-Shift-M powinien zadziałać :)


  • O mnie

    Maciej Grabek

    Moje profile na:

    MVP

    Codeguru.pl GoldenLine
    Twitter CodeProject

  • english version
  • Polecam

  • Copyright © Maciej Grabek. All rights reserved.
    Powered by WordPress
    %d bloggers like this: