Блог Сергея Байдачного

  • SilverLight 3: Local Connection

    Рассмотрим пространство имен System.Windows.Messaging, которое позволяет наладить взаимодействие между несколькими приложениями SilverLight.

    Создадим простое приложение, которое отображает эллипс двигающийся снизу вверх:

    <UserControl x:Class="SilverlightApplication57.MainPage"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Width="400" Height="300" Loaded=”UserControl_Loaded”>
        <UserControl.Resources>
            <Storyboard x:Name="sb1" Completed="sb1_Completed">
                <DoubleAnimation Storyboard.TargetName="el1"
                                 Storyboard.TargetProperty="(Canvas.Top)"
                                 From="300" To="-30" Duration="0:0:5">
                </DoubleAnimation>
            </Storyboard>
        </UserControl.Resources>
        <Canvas x:Name="LayoutRoot" Background="White">
            <Ellipse x:Name="el1" Width="30" Height="30" Fill="Blue" Canvas.Left="200" Canvas.Top="300"></Ellipse>
        </Canvas>
    </UserControl>

    Запуск анимации будет происходить при загрузке главного контейнера:

    private void UserControl_Loaded(object sender, RoutedEventArgs e)
    {
        sb1.Begin();
    }

    Создайте второе приложение, которое будет содержать точно такой же код, за исключением метода UserControl_Loaded. Разместите приложения на HTML странице следующим образом:

    image

    Давайте попробуем реализовать код прриложений таким образом, чтобы после окончания анимации в приложении 1, запускалась анимация во втором приложении. Таким образом, мы должны достигнуть эффета, когда наш шарик будет “перелетать” из одного приложения в другое.

    Реализуем следующий код в первом приложении:

    LocalMessageSender msgSender = new LocalMessageSender("DownSide");

    public MainPage()
    {
        InitializeComponent();

        LocalMessageReceiver receiver = new LocalMessageReceiver("UpSide");
        receiver.MessageReceived += new EventHandler<MessageReceivedEventArgs>(receiver_MessageReceived);
        receiver.Listen();
    }

    void receiver_MessageReceived(object sender, MessageReceivedEventArgs e)
    {
        sb1.Begin();
    }

    private void sb1_Completed(object sender, EventArgs e)
    {
        msgSender.SendAsync("start");
    }

    Тут мы создали объект типа LocalMessageSender, в задачи которого входит отправка сообщения “слушателю” с указанным именем (DownSide, наверное стоило назвать по другому). Этот метод отправляет сообщение сразу после окончания анимации с помощью метода SendAsync (фактически передает управление другому приложению).

    Объект LocalMessageReceiver, напротив, ожидает сообщение от других приложений. Как только сообщение приходит (второе приложение закончило анимацию), то тут же анимация запускается повторно (полетел еще один шарик).

    Код во втором приложении выглядит аналогично:

    LocalMessageSender msgSender = new LocalMessageSender("UpSide");

    public MainPage()
    {
        InitializeComponent();

        LocalMessageReceiver receiver = new LocalMessageReceiver("DownSide");
        receiver.MessageReceived += new EventHandler<MessageReceivedEventArgs>(receiver_MessageReceived);
        receiver.Listen();
    }

    void receiver_MessageReceived(object sender, MessageReceivedEventArgs e)
    {
        sb1.Begin();
    }

    private void sb1_Completed(object sender, EventArgs e)
    {
        msgSender.SendAsync("start");
    }

    Откомпилировав и запустив данный пример, мы получим желаемый эффект:

    image

    Следует отметить, что если Ваши приложения загружаются из различных доменов, то наладить взаимодействие между ними также возможно. Для этого просто следует указать имя домена в конструкторе классов LocalMessage…

  • SilverLight 3: Pixel API

    Кто-то на одном из семинаров спрашивал меня о том, как можно сделать скриншот интерфейса SilverLight приложения и сохранить его в виде изображения на диск. С этого примера и начнем.

    Для начала создадим интерфейс приложения, состоящий из примитивной формы и панели для отображения нашего снимка:

    <UserControl x:Class="SilverlightApplication56.MainPage"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Width="800" Height="600">
        <Grid x:Name="LayoutRoot" Background="White">
            <Grid.ColumnDefinitions>
                <ColumnDefinition></ColumnDefinition>
                <ColumnDefinition></ColumnDefinition>
            </Grid.ColumnDefinitions>
            <StackPanel Grid.Column="0">
                <TextBox Width="100"></TextBox>
                <TextBox Width="100"></TextBox>
                <Button Width="100" Content="Update" Click="Button_Click"></Button>
            </StackPanel>
            <StackPanel x:Name="stk1" Grid.Column="1" VerticalAlignment="Center">
            </StackPanel>
        </Grid>
    </UserControl>

    Чтобы пример был более эффектным, будем делать снимок не просто формы, а всего интерфейса, включая и панель со снимком. Чтобы реализовать такой код, нам понадобится класс WritableBitmap. Вот он то и представляет собой реализацию Pixel API в SilverLight 3. Этот класс содержится в пространстве имен System.Windows.Media.Imaging и позволяет создать Bitmap контекст заданного размера (длина, ширина изображения). Доступ к изображению может быть осуществлен попиксельно, используя простой индексатор.

    В нашем примере мы используем метод Render, который позволяет преобразовать в изображение любой графический элемент, включая его дочерние элементы.

    private void Button_Click(object sender, RoutedEventArgs e)
    {
        WriteableBitmap bit = new WriteableBitmap(this.Width, this.Height, PixelFormats.Pbgra32);
        bit.Render(LayoutRoot, new MatrixTransform());
        Image img = new Image();
        img.Source = bit;
        stk1.Children.Clear();
        stk1.Children.Add(img);
    }

    Вот так будет выглядить наше приложение после многократного нажатия кнопки Update.

    image

  • SilverLight 3: GPU Acceleration, RenderForAnimation, Smooth Streaming

    GPU Acceleration

    На всех семинарах мне задают один и тот же вопрос: Вы анонсировали GPU оптимизацию, но мы не смогли заметить разницу.

    На самом деле, если разработчик не выполнит ряд действий, которые почему-то пока не описаны в документации, никакой работы через GPU в его приложении не будет. Давайте посмотрим, что же необходимо сделать для включения оптимизации с помощью GPU.

    1). Оптимизация через GPU делает в SilverLight только первые шаги и не предоставляет пока много возможностей. На графический процессор можно пока расчитывать только в двух случаях: при отображении видео и при отображении растровых изображений. При этом речь идет не столько о простом отображении картинки или видео, как об использовании ряда эффектов.

    Фактически можно выделить три типа эффектов, которые поддерживаются в бета 1: а). трансформации изображения, б). урезание прямоугольной части изображения, с). смешивание пикселей в области медиа элемента или рисунка (отображение каки-то рекламных вставок, панели инструментов, управление прозрачностью).

    2). GPU поддерживается только на Windows платформе. Совершенно не важно, работает ваше приложение в окне браузера или в полноэкранном режиме. Если говорить о Mac, то тут поддержка осуществляется только в полноэкранном режиме. При этом ваша видеокарта должна поддерживать DirectX9.0C или выше.

    3). Для включения оптимизации с помощью GPU необходимо:

    а) задать эту опцию при создании плагина с помощью параметра EnableGPUAcceleration.

    <param name="EnableGPUAcceleration" value="true" />

    б) включить оптимизацию для конкретного элемента  управления. Это делается с помощью свойства CacheMode, которое должно быть установлено в значение BitmapCache.

    <MediaElement Source="a.wmv" Width="500" Height="300" CacheMode="BitmapCache">

    При этом свойство автоматически устанавливается и для всех дочерних элементов дерева.

    Если Вы хотите посмотреть, в каких из областей экрана работает оптимизация с помощью GPU добавьте еще один элемент к параметрам плагина:

    <param name=”EnableCacheVisualization” value=”true” />

    RenderForAnimation

    Раз уж я завел речь об оптимизации, то хочу сделать акцент на новом свойстве RenderOptions.TextRenderingMode, которое применимо к элементам, отображающим текст. Дело в том, что в SilverLight работает достаточно сложный процесс, который оптимизмрует отображение текста, занимаясь его сглиживанием. Но если вы используете текст для анимации, то выглядит это не всехгда впечатляюще. Часть фреймов могут теряться, текст выглядит подергивающимся. Свойство RenderOptions.TextRenderingMode, установленное в значение RenderForAnimation позволяет отключить оптимизацию при отображении текста и тем самым ускорить анимацию.

    Smooth Streaming

    Чтобы увидеть Smooth Streaming в действии, посетите сайт http://www.smoothhd.com/. Рекомендую перейти в раздел Blender и посмотреть видео Big Bucks Bunny:)

    image

    Итак, при доставке видео клиенту существует несколько проблем: 1). у пользователя может быть плохой канал, который недостаточен для отображения видео в текущем разрешении; 2). у пользователя может быть загружен процессор, что не позволит ему справится с отображением видео; 3). параметры нагрузки на канал и процессор пользователя регулярно меняются. Имея эти проблемы выложить несколько вариантов ролика, предназначенного на различные каналы явно недостаточно.

    Чтобы справится с  проблемой IIS 7 Media Service представляет специальное расширение Smooth Streaming.

    Идея Smooth Streaming состит в том, что Media service располагает сразу несколькими вариантами видео фрагмента (1280х720, 1024х768 и т. д.) и транслирует видео клиенту небольшими порциями, выбирая наиболее подходящий вариант, базируясь на параметрах канала.

    Естественно, чтобы доставлять видео хорошего разрешения с помощью Smooth Streaming необходимо преобразовать исходный файл, сгенерировав различные варианты потоков. Для этого можно использовать Microsoft Expression Encoder 2 SP1, который поддерживает специальный шаблон Adaptive Streaming и позволяет генерировать до 10 потоков.

    Далее остается только установить расширение в IIS и передать поток в SilverLight. MediaElement в SilverLight 3 уже поддерживает эту функциональность.

    Дополнительно о том, как настроить Smooth Streaming в IIS вы можете узнать по ссылке:

    http://www.iis.net/extensions/SmoothStreaming

  • SilverLight 3: Новые элементы управления

    Всю неделю колесил по стране и не мог писать. Получил один штраф за превышение скорости и помял четыре диска. Хорошо, что наши разработчики не такие как наши дороги. Но на этой неделе никаких поездок не планируется, поэтому пишу дальше.

    Рассматривая существующие элементы управления можно выделить несколько отдельных групп:

    • элементы управления, которые входят в стандартную поставку плагина – тут практически ничего не изменилось. О небольших изменениях для ListBox, тултипах, SaveFileDialog и валидаторе я писал ранее. С одной стороны в плагине есть все необходимое, а с другой – увеличение количества элементов привело бы к росту размера плагина;
    • элементы, которые входят в SDK – тут присутствует около двух десятков элементов управления, расширяющих существующую иерархию такими элементами, как DataGrid, Calendar и др. Если Вы используете эти элементы, то вместе со своим приложением необходимо поставлять сборку, в которой они расположены. Основные сборки, содержащие элементы управления, это System.Windows.Controls.dll, System.Windows.Controls.Data.DataForm.dll, System.Windows.Controls.Navigation.dll, System.Windows.Controls.Data.dll, System.Windows.Controls.Input.dll;
    • элементы, которые входят в Toolkit, развиваемый как OpenSource проект на codeplex.com (http://www.codeplex.com/silverlight). Нужно отметить, что многие из этих элементов мигрируют в официальный SDK сразу после стабилизации, но Toolkit развивается довольно динамично, поэтому разработчики могут найти что-то интересное для себя;
    • платные элементы управления – сегодня существует множество компаний, которые предлагают свои библиотеки.

    В этой статье мы поговорим о новых элементах, которые были добавлены в SDK.

    Простые элементы управления

    ChildWindow

    Элемент ChildWindows позволяет Вам достаточно оперативно создать всплывающее окно, блокирующее весь остальной интерфейс. Если Вы используете Visual Studio, то для создания простейшего окна достаточно добавить в проект новый элемент на основе шаблона SilverLight Child Window. Данный шаблон создает XAML файл, описывающий Child окно и ассоциированный с ним код:

    .XAML файл

    <controls:ChildWindow x:Class="SilverlightApplication47.ChildWindow1"
               xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
               xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
               xmlns:controls="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls"
               Width="400" Height="300"
               Title="ChildWindow1">
        <Grid x:Name="LayoutRoot" Margin="2">
            <Grid.RowDefinitions>
                <RowDefinition />
                <RowDefinition Height="Auto" />
            </Grid.RowDefinitions>

            <Button x:Name="CancelButton" Content="Cancel" Click="CancelButton_Click" Width="75" Height="23" HorizontalAlignment="Right" Margin="0,12,0,0" Grid.Row="1" />
            <Button x:Name="OKButton" Content="OK" Click="OKButton_Click" Width="75" Height="23" HorizontalAlignment="Right" Margin="0,12,79,0" Grid.Row="1" />
        </Grid>
    </controls:ChildWindow>

    .CS файл

    public partial class ChildWindow1 : ChildWindow
    {
        public ChildWindow1()
        {
            InitializeComponent();
        }

        private void OKButton_Click(object sender, RoutedEventArgs e)
        {
            this.DialogResult = true;
        }

        private void CancelButton_Click(object sender, RoutedEventArgs e)
        {
            this.DialogResult = false;
        }
    }

    Инициировать вызов такого окна достаточно просто:

    ChildWindow1 child = new ChildWindow1();
    child.Show();

    image

     

    Элемент Expander, HeaderedContentControl, HeaderedItemsControl

    Следующий элемент представляет собой распахивающуюся панель.

    <controls:Expander Header="This is a header" FontSize="18" BorderThickness="1" BorderBrush="Gray" >
        <TextBlock Text="This is a text"></TextBlock>   
    </controls:Expander>

    image

    Поскольку Expander является наследником от HeaderedContentControl, Вы можете использовать более сложный формат для создания заголовка и содержимого:

    <controls:Expander BorderThickness="1" BorderBrush="Gray" >
        <controls:HeaderedContentControl.Header>
            <TextBlock Text="This is a header" FontSize="18"></TextBlock>
        </controls:HeaderedContentControl.Header>
        <controls:HeaderedContentControl.Content>
            <TextBlock Text="This is a text"></TextBlock>
        </controls:HeaderedContentControl.Content>   
    </controls:Expander>

    Используя этот механизм Вы можете установить в качестве заголовка любое содержимое.

    Элемент HeaderedContentControl можно использовать как самостоятельный элемент:

    <controls:HeaderedContentControl  >
        <controls:HeaderedContentControl.Header>
            <TextBlock Text="This is a header" FontSize="18"></TextBlock>
        </controls:HeaderedContentControl.Header>
        <controls:HeaderedContentControl.Content>
            <TextBlock Text="This is a text"></TextBlock>
        </controls:HeaderedContentControl.Content>   
    </controls:HeaderedContentControl>

    Если Вы хотите отобразить набор повторяющихся элементов с заголовком, Вы можете использовать HeaderedItemsControl:

    <controls:HeaderedItemsControl>
        <controls:HeaderedItemsControl.Header>
            <TextBlock Text="This is a header" FontSize="18"></TextBlock>
        </controls:HeaderedItemsControl.Header>
        <controls:HeaderedItemsControl.Items>
            <TextBlock Text="Item 1"></TextBlock>
            <TextBlock Text="Item 2"></TextBlock>
        </controls:HeaderedItemsControl.Items>   
    </controls:HeaderedItemsControl>

    Пример выше вряд ли может показаться интересным, а вот биндинг с динамическим набором элементов значительно интересней:

    itemsCol.ItemsSource = new string[] { "Sunday", "Monday", "Tuesday" };

    <controls:HeaderedItemsControl x:Name="itemsCol">
        <controls:HeaderedItemsControl.Header>
            <TextBlock Text="This is a header" FontSize="18"></TextBlock>
        </controls:HeaderedItemsControl.Header>
        <controls:HeaderedItemsControl.ItemTemplate>
            <DataTemplate>
                <TextBlock Text="{Binding}"></TextBlock>
            </DataTemplate>
        </controls:HeaderedItemsControl.ItemTemplate>   
    </controls:HeaderedItemsControl>

    Элемент TreeView

    Следующий элемент управления, это TreeView. Как и другие элементы управления TreeView позволяет в качестве элементов дерева использовать любой контент.Вот пример использования элемента.

    <controls:TreeView>
        <controls:TreeViewItem IsExpanded="True">
            <controls:TreeViewItem.Header>
                <TextBlock Text="Hello"></TextBlock>
            </controls:TreeViewItem.Header>
            <controls:TreeViewItem Header="SubItem">
            </controls:TreeViewItem>
        </controls:TreeViewItem>
    </controls:TreeView>

    Элемент AutoCompleteBox

    В отличии от предыдущих элементов, элемент AutoCompleteBox находится в сборке System.Windows.Controls.Input.dll. Этот элемент позволяет отобразить поле для ввода текста и предоставляет возможность динамически установить массив строк для отображения подсказок и автоматического заполнения. Вот пример использования:

    box.ItemsSource = new string[] { "Sunday", "Monday", "Tuesday" };

    <input:AutoCompleteBox Width="100" x:Name="box"></input:AutoCompleteBox>

    Элемент Label

    Элемент управления Label позволяет отобразить “надпись”. В отличии от TextBlock элемент Label способен отображать любой контент в качестве метки. Вот пример использования:

    <controls:Label Content="TreeView" />

  • Новое видео с Дней разработчиков в Киеве

  • SilverLight 3: Что нового? (часть 3)

    Наконец я прибыл в Минск. Через полтора часа у меня встреча и большая надежда на Интернет. Уже сейчас узнал, что анонс SilverLight 3 таки состоялся и даже появилась документация (вы не поверите, но у меня ее еще нет). Была слабая надежда на то, что тут есть платный зал ожидания. Он таки есть, но туда лучше не ходить. В обычном зале ожидания стулья железные и холодные. Ничего, я слышал, что теплые вредно, а холодные не дадут заснуть. Пишем дальше:)

    Проверка данных при связывании

    Рассмотрим небольшой пример. Для этого опишем простой класс Employee с двумя полями:

        public class Employee
        {
            private string firstName;

            public string FirstName
            {
                get { return firstName; }
                set
                {
                    if (!value.Equals("Sergey"))
                    {
                        throw new Exception("First Name must be Sergey!");
                    }
                    firstName = value;
                }
            }
            private string lastName;

            public string LastName
            {
                get { return lastName; }
                set { lastName = value; }
            }

            public Employee(string fName, string lName)
            {
                this.FirstName = fName;
                this.LastName = lName;
            }
        }

    А также опишем форму, которая связана с объектом типа Employee

    <StackPanel x:Name="LayoutRoot" Background="White" Loaded="LayoutRoot_Loaded">
        <TextBox Width="100" Text="{Binding FirstName, Mode=TwoWay}"></TextBox>
        <TextBox Width="100" Text="{Binding LastName, Mode=TwoWay}"></TextBox>
    </StackPanel>

    И код:

    Employee emp;

    private void LayoutRoot_Loaded(object sender, RoutedEventArgs e)
    {
        emp = new Employee("Sergey","B");
        LayoutRoot.DataContext = emp;
    }

    Как видно из реализации класса Employee, в качестве имени может быть установленно только Sergey. В SilverLight 3 есть два хороших механизма по обработке ошибок при связывании с данными.

    Первый механизм, это специальный элемент управления, который будет нотифицировать пользователя об ошибке связывания. Для этого внесем небольшие изменения в описание TextBox.

    <TextBox Width="100" Text="{Binding FirstName, Mode=TwoWay, ValidatesOnExceptions=true}"></TextBox>

    Мы добавили простое свойство ValidatesOnException. Чтобы посмотреть на его работу, запустите приложение и введите неверные данные. Поле ввода будет выделено красной рамкой, а при наведении фокуса Вы увидете сообщение, которое заключено в исключении.

    image

    Второй механизм позволяет написать код в ответ на исключение при связывании. Для этого изменим TextBox следующим образом:

    <TextBox Width="100" Text="{Binding FirstName, Mode=TwoWay, ValidatesOnExceptions=true, NotifyOnValidationError=true}" BindingValidationError="TextBox_BindingValidationError" x:Name="textBox1"></TextBox>

    Тут мы описали обработчик специального события BindingValidationError, а также указали свойство NotifyOnValidationError, которое говорит о необходимости генерации этого события.

    А вот и код обработчика:

    private void TextBox_BindingValidationError(object sender, ValidationErrorEventArgs e)
    {
        textBox1.Focus();
    }

    Тут мы просто устанавливаем фокус на наше поле, чтобы дополнительно пивлечь внимание пользователя и заставить его исправить ошибку.

    Все, холод таки вреден. А тут еще и снегопад жуткий. Может и не доберусь уже до Интернета, никогда.... Попробую выпить кофе и пойти на встречу. Продолжение следует.

  • SilverLight 3: Что нового? (часть 2)

    На прошлой неделе со мной произошло большое несчастье. Приехав в Киев в субботу ночью, я обнаружил, что забыл блок питания от лаптопа в Днепропетровске. Как назло в офисе чистили ковры и никого не пускали. Поэтому на воскресенье я остался без лаптопа. В результате этого несчастья я обнаужил, что в Киеве тоже есть супермаркеты, а у меня в квартире несколько кастрюль и плита. Закончил я готовить ровно тогда, когда еда перестала помещаться в холодильник. Теперь можно есть месяц. Вот как раз сейчас я доел очередной кусок курицы, закусил бутербродом с салом и могу приступить ко второй части своей статьи. Поскольку я все еще еду в Минск, то очень трясет, поэтому без ошибок не обойтись.

    SaveFileDialog

    В предыдущих версиях SilverLight Вы могли общаться с файловой системой только через OpenFileDialog. Это вызывало много нареканий от разработчиков, так как при отображении текстовой информации пользователь не имел возможности сохранить ее на локальный диск. Это же касалось различных графических редакторов и т. д. Теперь разработчик имеет возможность записать на диск любую информацию и в любое место, используя возможности SaveFileDialog. Смотрим пример:

    SaveFileDialog file= new SaveFileDialog();
    file.Filter = "Text File | *.txt";
    file.DefaultExt = ".txt";
    file.ShowDialog();

    if (file.File.Name != "")
    {
         System.IO.StreamWriter s =
             new System.IO.StreamWriter(file.OpenFile());
         s.Write("Hello");
         s.Close();
    }

    Этот код можно разместить в любом методе интерфейсного потока. В результате работы кода, пользователю будет предложено выбрать папку для записи и имя файла (по умолчанию .txt). В файл будет записано Hello. Ничего сложного.

    Поддержка ToolTip

    Следующий интересный элемент управления, это ToolTip. Он может быть привязан к любому UIElement и содержать практически любой контент для отображения. Привязку к элементу можно выполнять, как указав свойство PlacementTarget, так и использовав этот элемент в качестве части контента. Ниже показан код, который позволяет отобразить ToolTip для кнопки. В качестве контента тут используется видео-файл:

    <Button Content="Detach" Width="100" Click="Button_Click">
      <ToolTipService.ToolTip>
        <ToolTip Placement="Right">
           <ToolTip.Content>
             <MediaElement Source="4.wmv"></MediaElement>
           </ToolTip.Content>
        </ToolTip>
      </ToolTipService.ToolTip>
    </Button>

    ToolTip может быть привязан к одной из границ элемента или к координатам курсора мыши. Привязка задается с помощью свойства Placement.

    CaretBrush

    Еще одной небольшой особенностью SilverLight 3 является возможность установки каретки для текстового поля и поля для задания пароля (TextBox PasswordBox). Дело в том, что элементы в SilverLight реализуются таким образом, что представление отделено от логики. Поэтому разработчик может всегда заменить внешний вид уже существующего элемента. Например, можно создать треугольную кнопку или овальное поле для ввода данных. Но в элементах TextBox и PasswordBox одна часть не поддавалась изменениям - каретка. Она была исключительно черной. Даже, если разработчик просто меня цвет поля ввода, каретку не всегда было видно. Теперь ситуация изменилась. Теперь каретка может использовать любую из доступных кистей для заливки. Разработчик может просто поменять цвет, а может и установить видеоролик в качестве каретки. Вот пример использования свойства:

    <TextBox Text="Text" CaretBrush="Blue"></TextBox>
    <TextBox Text="Text" >
        <TextBox.CaretBrush>
            <LinearGradientBrush StartPoint="0,0" EndPoint="0,1">
                <GradientStop Color="Yellow" Offset="0.0" />
                <GradientStop Color="Red" Offset="0.25" />
                <GradientStop Color="Blue" Offset="0.75" />
                <GradientStop Color="LimeGreen" Offset="1.0" />
            </LinearGradientBrush>
        </TextBox.CaretBrush>
        <TextBox.RenderTransform>
            <ScaleTransform ScaleX="3" ScaleY="3"/>
        </TextBox.RenderTransform>
    </TextBox>

    Множественный выбор в ListBox

    Поскольку как-то так получилось, что мы начали вести речь об элементах управления, рассмотрим еще один, уже знакомый элемент - ListBox. Тут появилось свойство SelectionMode. Это свойство может содержать одно из трех значений: Single, Multiple, Extended. При установке свойства Multiple пользователь может выбирать несколько значений, простым кликом, а при выборе Extended также доступны функциональные клавиши и поведение больше похоже на полюбившийся ListBox из Forms или WPF.

    <ListBox x:Name="listBox1" SelectionMode="Extended" SelectionChanged="ListBox_SelectionChanged">
        <ListBox.Items>
            <ListBoxItem Content="Item 1"></ListBoxItem>
            <ListBoxItem Content="Item 2"></ListBoxItem>
            <ListBoxItem Content="Item 3"></ListBoxItem>
            <ListBoxItem Content="Item 4"></ListBoxItem>
        </ListBox.Items>
    </ListBox>
    <TextBlock x:Name="textBox1"></TextBlock>

    private void ListBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
    {
        textBox1.Text = String.Format("{0} items selected",listBox1.SelectedItems.Count);
    }

    На этом я на сегодня закончу, так как очень хочу спать. Завтра напишу что-то еще.

  • SilverLight 3: Что нового? (часть 1)

    В то время, как в Лас-Вегасе открывается конференция MIX09, я еду в поезде в сторону Минска, где планирую сделать первый доклад о технологии SilverLight 3. К тому моменту, как я сойду с поезда, подключусь в Интернет и опубликую эту статью, Вы уже должны иметь возможность загрузить этот продукт из сети в версии Beta 1. Кроме того, если Вы не смогли попасть в Лас-Вегас (может мы едем в одном поезде), то сможете получить доступ к записанным видео-трансляциям с конференции в течении следующих 24 часов. Именно по этой причине я не решаюсь дать краткий обзор возможностей SilverLight 3, а сразу перейду к написанию кода, сосредоточившись лишь на некоторых аспектах (пока меня не отвлекут таможенники с вопросом "Что везете? Сервелат? 3 ящика? А колбасу нужно сдать.").

    Итак,...

    3D проекции

    Естественно, что реализовать механизм полноценного 3D движка в плагине размером 4-6 Мб достаточно тяжело. Между тем псевдо 3D позволяет реализовать множество различных сценариев, но требует от разработчика значительное количество усилий. Благодаря элементу PlaneProjection в SilverLight 3 разработчик может стоить простые проекции, указав всего несколько параметров. Рассмотрим простой код, отображающий MediaElement:

    <UserControl x:Class="SilverlightApplication30.MainPage"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
        <StackPanel x:Name="LayoutRoot" Background="White" VerticalAlignment="Center">
            <MediaElement Source="4.wmv" Width="500" Height="300">
                <MediaElement.Projection>
                    <PlaneProjection x:Name="projection" RotationY="60"></PlaneProjection>
                </MediaElement.Projection>
            </MediaElement>
        </StackPanel>
    </UserControl>

    В результате мы получим MediaElement "повернутый" на 60 градусов по оси Y.

    image

    Тут мы использовали элемент PlaneProjection с атрибутом RotationY. Логично, что если Вы хотите развернуть изображение по другим осям, то нужно воспользоваться атрибутами (свойствами) RotationX, RotationZ. Элемент PlaneProjection можно использовать по отношению к любому из элементов-наследников от UIElement.

    Добавим к нашему коду 4 слайдера, чтобы изменять угол вращения по запросу пользователя:

    <UserControl x:Class="SilverlightApplication30.MainPage"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
        <StackPanel x:Name="LayoutRoot" Background="White" VerticalAlignment="Center">
            <MediaElement Source="4.wmv" Width="500" Height="300">
                <MediaElement.Projection>
                    <PlaneProjection x:Name="projection" ></PlaneProjection>
                </MediaElement.Projection>
            </MediaElement>
            <Slider Width="588" Minimum="0" Maximum="360" Value="{Binding RotationX, Mode=TwoWay, ElementName=projection}"></Slider>
            <Slider Width="588" Minimum="0" Maximum="360" Value="{Binding RotationY, Mode=TwoWay, ElementName=projection}"></Slider>
            <Slider Width="588" Minimum="0" Maximum="360" Value="{Binding RotationZ, Mode=TwoWay, ElementName=projection}"></Slider>
            <Slider Width="588" Minimum="0" Maximum="300" Value="{Binding LocalOffsetZ, Mode=TwoWay, ElementName=projection}"></Slider>
        </StackPanel>
    </UserControl>

    В этом примере мы воспользовались еще одним свойством LocalOffsetZ. Свойства LocalOffsetX, LocalOffsetY, LocalOffsetZ позволяют разместить объект в трехмерном пространстве. Как пример использования этих свойств можно рассмотреть "карусель", где все объекты кроме одного расположны на некотором удалении. Кроме этих свойств существуют еще свойства GlobalOffsetX, GlobalOffsetY, GlobalOffsetZ. Разница между ними только в том, что первые устанавливают смещение относительно текущего контейнера, а вторые - относительно всего окна.

    Последние свойства, которые могут быть интересны разработчику, это CenterOfRotationX, CenterOfRotationY, CenterOfRotationZ. Позволяют задать точку, вокруг которой проводить вращение.

    О! Мы тронулись и ко мне в шкафчик пришел какой-то бородатый мужик. Мужик - не девушка, поэтому я еще смогу не отвлекаться. Кроме того, он говорит только по английски. Сделаю вид, что я не есть понимать.

    Offline работа

    Следующая интересная возможность - установка SilverLight приложения в локальный кэш пользователя с возможностью запуска в режиме, отключенном от сети.

    Для того, чтобы установить приложение в кэш на машине пользователя, необходимо выполнить два шага:

    1). Вызвать метод Detach у объекта Application. При этом данный метод должен быть вызван только из обработчика события, инициируемого пользователем. Например, событие может быть связано с нажатием на кнопку.

    Добавим кнопку в предыдущий XAML файл.

    <Button Content="Detach" Width="100" Click="Button_Click"></Button>

    Разместите кнопку сразу после последнего элемента Slider.

    Обработчик нажатия на кнопку будет выглядеть так:

    private void Button_Click(object sender, RoutedEventArgs e)
    {
        Application.Current.Detach();
    }

    2). Добавить несколько элементов в манифест приложения. Эти элементы необходимы в первую очередь для того, чтобы предоставить дополнительные параметры, которые нужны при развертывании приложения на клиент (надписи, иконки). Вот как будет выглядеть наш манифест:

    <Deployment xmlns="http://schemas.microsoft.com/client/2007/deployment"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
        <Deployment.Parts>
        </Deployment.Parts>
        <Deployment.ApplicationIdentity>
            <ApplicationIdentity
                ShortName="Offline App"
                Title="My Title">
                <ApplicationIdentity.Blurb>
                  This is my offline App
                </ApplicationIdentity.Blurb>
            </ApplicationIdentity>
        </Deployment.ApplicationIdentity>
    </Deployment>

    Если Вы хотите также описать и иконки, то добавьте следующий элемент:

    <ApplicationIdentity.Icons>
       <Icon Size="16x16">icons/16x16.png</Icon>
       <Icon Size="32x32">icons/32x32.png</Icon>
       <Icon Size="48x48">icons/48x48.png</Icon>
       <Icon Size="128x128">icons/128x128.png</Icon>
    </ApplicationIdentity.Icons>

    Если иконки явно не указаны, то подставляется иконка по умолчанию.

    Теперь можно запустить приложение и нажать на кнопку. На экране появится следующее сообщение:

    image 

    Итак, пользователь выбирает, где он хочет видеть иконку своего приложения и должен нажать ОК для установки приложения в кэш. Нужн отметить, что для установки приложения в кэш пользователь НЕ должен обладать административными правами.

    Для удаления приложения пользователь должен инициировать вызов контекстного меню (раньше можно было вызвать только SilverLight Configuration) и выбрать Remove This Application. При этом команда доступна вне зависимости от того запустил пользователь локальную версию или воспользовался браузером.

    Работая с приложение в отключенном режиме программист имеет возможность определять метод запуска приложения, а также реагировать на изменение метода запуска. Во-первых в объекте Application всегда доступно свойство ExecutionState, через которое можно получить значение перечислимого типа ExecutionStates. Последнее может содержать следующие значения: Detached, DetachedUpdatesAvailable, DetachedFailed, Detaching, RunningOnline. Во-вторых, есть возможность реагировать на событие приложения Application_ExecutionStateChanged.

    Работая в отключенном режиме, разработчик может определить состояние подключения. Это может быть очень полезно при реализации механизма синхронизации данных. Проверить соединение можно с помощью вот этого статического метода: NetworkInterface.GetIsNetworkAvailable(). Обращаю Ваше внимание на то, что класс NetworkInterface находится в пространстве имен System.Net.NetworkInformation.

    Обновление приложения в разъединенном окружении

    Что же происходит, если приложение было обновлено в сети? Оказывается приложение автоматически проверяет, доступны ли обновления. Это происходит сразу после того, как приложение обнаружило доступ в сеть. Если обновления обнаружены, то они тут же начинают загружаться в кэш пользователя, после чего происходит событие ExecutionStateChanged. Обработав это событие, можно нотифицировать пользователя о том, что обновление уже доступно и ему нужно перезапустить приложение. Какой-либо возможности управлять загрузкой обновлений или отключить эту функциональность (ключи в реестре не считаются) - нет.

    Реакция на изменение сетевого интерфейса

    Оказывается, что оперативно определять разрыв связи с сетью можно с помощью реакции на статическое событие NetworkAddressChanged, которое описано в классе NetworkChange. Это событие происходит всякий раз, когда с сетевым интерфейсом что-то происходит. Далее задача программиста воспользоваться методом GetIsNetworkAvailable, чтобы понять, есть ли соединение.

    Вот и все, что можно на текущий момент сказать об отключенном режиме. Принесли чай, поэтому я завершаю эту короткую статью и начинаю искать свои бутерброды.

  • Deep Dive курсы по технологии SilverLight (Симферополь, Харьков)

    Мы запустили цикл курсов по SilverLight 2. Курс расчитан на 2 дня и уже можно зарегистрироваться для Симферополя и Харькова.

    Вот ссылки:

    http://msevents.microsoft.com/CUI/EventDetail.aspx?EventID=1032406250&Culture=UK-UA

    http://msevents.microsoft.com/CUI/EventDetail.aspx?EventID=1032406251&Culture=UK-UA

  • Последние доклады с Дней разработчиков

  • Windows SharePoint Services 3.0: Создание и развертывание решений (часть 6)

    Последняя и самая сложная тема, которую мы рассмотрим применительно к Решениям, это локализация.

    Начнем с того, что рассмотрим какие типы ресурсов встречаются в WSS 3.0. Условно все ресурсы можно разбить на два типа:

    1). ресурсы, которые используются в сборках Вашего Шаблона, сборках веб-частей и т. д. В этом случае файлы с ресурсами должны быть откомпилированы и встроены в вашу сборку, если это ресурсы по умолчанию, либо разместиться в GAC в виде отдельной сборки с характерной культурой.

    При создании этого типа ресурсов механизм очень прост. Воспользуйтесь Visual Studio и добавьте файл ресурсов в проект, например NMUPortal.resx. Visual Studio автоматически создаст прокси-класс, который упростит доступ к ресурсам и позволит получать доступ к записям, используя соответствующие имена переменных. Например, если создать запись с именем Portal_Title, то доступ к этой записи из кода можно будет получить следующим образом:

    NMUPortal.Resources.NMUPortal.Portal_Title

    Если Вы хотите добавить ресурсы для конкретной культуры, используйте в имени файла идентификатор культуры. Например для русского языка файл ресурсов будет иметь имя NMUPortal.ru-RU.resx.

    Visual Studio ресурсы по умолчанию встраивает в сборку, а ресурсы, соответствующие культурам, компилирует в отдельные сборки и разбрасывает их по соответствующим папкам. Таким образом, Вы развертываете на сервере ни одну сборку с кодом, но и сборки с ресурсами. В зависимости от установленной культуры пользователя, данные будут выбираться из соответствующей сборки.

    Если ресурсы используются внутри сборки, то их развертывание в GAC осуществляется с помощью уже знакомого элемента Assembly в manifest.xml.

    2). ресурсы, которые используются во время обработки CAML-файлов (onet.xml, feature.xml и т. д.) и при компиляции страниц приложения и элементов управления. В этом случае ресурсы не компилируются, а поставляются в исходном виде (XML).

    Тут все намного сложнее, так как нет единого места для таких ресурсов.

    а). Если Вы используете ресурсы в страницах приложений или элементах управления, то все они должны располагаться в папке App_GlobalResources веб-приложения. Как ни странно, но если приложение уже создано, то после развертывания шаблона их нужно скопировать в App_GlobalResources самостоятельно. В теории это должен был бы делать элемент ApplicationResource в манифесте, но он создает папку resources и копирует все туда. Поэтому использование последнего для меня до сих пор большая загадка. В общем же случае, чтобы ресурсы попали в папку вновь созданного веб-приложения, их нужно разместить в C:\Program Files\Common Files\Microsoft Shared\Web Server Extensions\12\CONFIG\Resources. Это можно сделать с помощью элемента RootFile в манифесте.

    <RootFiles>
      <RootFile Location="Config\Resources\NMUPortal.resx"/>
    </RootFiles>

    Использовать ресурсы довольно просто. Например:

    <asp:Label runat=server text="<%$Resources:NMUPortal,Portal_Title %>"></asp:Label>

    б). Если Вы хотите использовать ресурсы в onet.xml или features.xml, то их нужно скопировать в другие папки. Глобальной папкой является C:\Program Files\Common Files\Microsoft Shared\Web Server Extensions\12\Resources. Если ресурсы находятся тут, то они доступны и в onet.xml и в других CAML файлах.

    Пример использования:

    <NavBars>
      <NavBar Body="$Resources:NMUPortal,Portal_Title" ID="1212" Name="$Resources:NMUPortal,Portal_Title" Url="http://www.microsoft.com"></NavBar>
    </NavBars>

    Развернуть сюда ресурсы можно с помощью того же элемента RootFile

    <RootFiles>
      <RootFile Location="Resources\NMUPortal.resx"/>
    </RootFiles>

    c). Если Вы планируете использовать ресурсы только с конкретной Feature, то было бы логично развернуть их в директории Feature. Это можно сделать с помощью элемента Resources в манифесте:

    <Resources>
      <Resource Location="AddNewsSiteAction\Resources\NMUPortal.resx"/>
    </Resources>

    В теории их можно развернуть в любую директорию внутри директории соответствующей Feature.

    <?xml version="1.0" encoding="utf-8" ?>
    <Feature DefaultResourceFile="_res"
             xmlns="http://schemas.microsoft.com/sharepoint/"
             Id="91E266BE-CAFB-4197-B920-31E6627C2056"
             Title="Add News"
             Description="Add news to the main page"
             Scope="Site"
             Hidden="FALSE"
             ImageUrl="menuprofile.gif"
             >
      <ElementManifests>
        <ElementManifest Location="elements.xml"/>
      </ElementManifests>
    </Feature>

    DefaultResourceFile как раз и позволяет установить директорию. Если этот атрибут не установлен или _res, то используется директория Resources внутри внутри директории Feature. А вот пример использования:

    <?xml version="1.0" encoding="utf-8" ?>
    <Elements xmlns="http://schemas.microsoft.com/sharepoint/">
      <CustomAction Id="AddNewsAction"
                    GroupId="SiteActions"
                    Location="Microsoft.SharePoint.StandardMenu"
                    Sequence="100"
                    Title="$Resources:Portal_Title;"
                    Description="Manage news"
                    ImageUrl="_layouts/images/menuprofile.gif">
        <UrlAction Url="_layouts/NMUPortal/MyApplicationPage.aspx" />
      </CustomAction>
    </Elements>

    Вот и все, что касается ресурсов.

    Последний компонент, который требуется локализовать, это webtemp*.xml. Он не может использовать ресурсы. Поэтому для его локализации требуется создавать несколько webTemp*.xml - для каждой культуры. При этом их нужно разворачивать в директорию с иденетификатором соответствующей культуры (вместо 1033 номер вашей культуры).

    И теперь о главном - как все это развернуть. Механизм тут следующий:

    Шаг 1. Разверните базовый шаблон, не включая ресурсы, соответствующие конкретной культуре.

    Шаг 2. Для каждого типа культуры создайте отдельный пакет (его принято называть Language Pack). В manifest.xml этого пакета включите все ресурсы и webtemp*.xml.

    Шаг 3. Разверните Language Pack с использованием stsadm:

    %stsadm% -o addsolution -filename  %WSPackage% -lcid 1049
    %stsadm% -o execadmsvcjobs
    %stsadm% -o deploysolution -name NMUPortal.wsp -immediate -allowGacDeployment -force -lcid 1049
    %stsadm% -o execadmsvcjobs

    Тут мы добавили lcid для спецификации культуры.

    Удаление решения происходит в обратном порядке (сначало удаляем все Language Packs, а затем основное решение)

  • Windows SharePoint Services 3.0: Создание и развертывание решений (часть 5)

    В предыдущих частях мы рассматривали процесс создания простого решения. Как видно, большинство параметров решения можно задать в onet.xml. Однако есть задачи, которые нельзя реализовать путем установки элементов в конфигурационном файле. Например, Вы хотите создать сразу целую коллекцию сайтов. В этом случае Вам нужно иметь возможность выполнять код при создании сайта на основе шаблона.

    Ассоциация кода с шаблоном задается в файле webtemp*.xml при описании конфигурации. Например, если мы хотим ассоциировать код с нашим шаблоном NMUPortal, то конфигурация будет выглядеть следующим образом:

    <Configuration
      ID="1"
      Hidden="FALSE"
      Title="National Mining University Portal"
      Description="Create a National Mining University Portal"
      ImageUrl="/_layouts/images/BlankPrev.png"
      DisplayCategory="NMU Projects"
      RootWebOnly="true"
      SubWebOnly="false"
      ProvisionAssembly="NMUPortal, Version=1.0.0.0, Culture=neutral, PublicKeyToken=e1ffca03d8df0be6"
      ProvisionClass="NMUPortal.NMUPortalProvision"
      ProvisionData="My Title"/>

    Тут используются свойства ProvisionAssembly, ProvisionClass, описывающие сборку и класс, который будет использоваться прри создании сайта. Сборка должна быть развернута в GAC. ProvisionData может содержать текстовыю строку, из которой код сможет извлекать параметры.

    Основное требование к классу - он должен быть наследником от SPWebProvisioningProvider. Вот пример такого класса:

    using System;
    using Microsoft.SharePoint;

    namespace NMUPortal
    {
        class NMUPortalProvision: SPWebProvisioningProvider
        {
            public override void Provision(SPWebProvisioningProperties props)
            {
                props.Web.ApplyWebTemplate("NMUPortal#0");
                SPSecurity.RunWithElevatedPrivileges(delegate()
                {
                    using (SPSite siteCol = new SPSite(props.Web.Site.ID))
                    {
                        using (SPWeb site = siteCol.OpenWeb(props.Web.ID))
                        {
                            site.Title = props.Data;
                            site.Update();
                        }
                    }
                });
            }
        }
    }

    Фактически единственным методом для перегрузки является Provision. Тут Вы можете создавать новые сайты, заполнять списки данными из БД и т. д. Но для этого Вы должны выполнить два действия:

    1). вызвать ApplyWebTemplate, который позволит все-таки применить какой-то из шаблонов к создаваемому сайту. Ведь Вы перегрузили метод Provision и теперь сайт, на основе выбранного шаблона, не будет создан автоматически. Самое важное в этом месте, это не сделать ошибку и не указать в качестве шаблона ту конфигурацию, к которой привязан Ваш код. Произойдет зацикливание. Поэтому заранее подготовьте конфигурацию для создания главного узла:

    <Configuration
       ID="0"
       Hidden="TRUE"
       Title="National Mining University Portal"/>

    Естественно эту конфигурацию нужно описать и в onet.xml.

    2). Поскольку код выполняется под аккаунтом обычного пользователя, необходимо изменить контекст безопасности, дав коду административные права. Это можно сделать с помощью метода RunWithElevatedPrivileges, который в качестве параметров получает метод для выполнения в контексте администратора.

    Замечание. При работе с SPSite и SPWeb классами используйте using или явно вызывайте Dispose.

    Вот и все, что необходимо сделать для выполнения кода в момент создания сайта на основе шаблона.

    В следующей части мы поговорим о локализации и ресурсах. На этом и закончим тему решений.

  • Windows SharePoint Services 3.0: Создание и развертывание решений (часть 4)

    Рассмотрим процесс добавления веб-части в наше решение. Для этого добавьте в решение новый класс и введите следующий код:

    using System;
    using System.Web.UI.WebControls.WebParts;

    namespace NMUPortal
    {
        public class HelloWebPart : WebPart
        {
            protected override void RenderContents(System.Web.UI.HtmlTextWriter writer)
            {
                writer.Write("Hello World! "+Context.User.Identity.Name);
                base.RenderContents(writer);
            }
        }
    }

    В отличии от WSS 2.0 в новой версии Вы можете использовать самые обычные веб-части, созданные на основе класса System.Web.UI.WebControls.WebParts.WebPart. При этом механизм создания ничем не отличается от механизма в ASP.NET 2.0. Между тем старый класс Microsoft.SharePoint.WebPartPages.WebPart также сохранился и его можно использовать, но для этого необходимо добавить ссылку на сборку Microsoft.SharePoint.dll, которая находится в папке C:\Program Files\Common Files\Microsoft Shared\Web Server Extensions\12\ISAPI. Принято считать, что старый класс сохранили лишь для обеспечения совместимости с ранее разработанными веб-частями, но если Вы попробуете разработать веб-часть для размещения на служебных страницах или страницах, ассоциированных со списками (вот только зачем?), то столкнетесь с необходимостью использования Microsoft.SharePoint.WebPartPages.WebPart.

    Если Вы создали проект в Visual Studio на основе шаблона Control Library, то для создания сборки можно воспользоваться командой Build. Но прежде чем компилировать код, необходимо настроить ряд параметров, которые позволят подготовить сборку для развертывания в Глобальный кэш сборок (GAC). (GAC представляет собой универсальное хранилище для сборок, которые доступны всем приложениям. Единой директории для GAC не существует, а развертывание сборки в GAC осуществляется не с помощью копирования, а с помощью специальной утилиты gacutil.). Чтобы сборка могла быть развернута в GAC, у нее должно быть строгое имя, которое определяется культурой, версией,именем сборки, а также цифровой подписью. Все эти данные хранятся в файле AssemblyInfo.cs Вашего проекта, исключая ключ для подписи. Чтобы сгенерировать ключ, необходимо запустить утилиту sn.exe. Это можно сделать из командной строки Visual Studio. Следующая команда сгенерирует нужный файл:

    sn.exe -k mykey.snk

    Теперь чтобы подписать сборку можно воспользоваться окном Свойств проекта.

    Приступим к внесению изменений в Решение. Поскольку код нашей веб-части содержится в сборке, то сборку необходимо развернуть в GAC (или поместить в bin директорию приложения), а также сделать изменения в конфигурационном файле приложения. Так сборка не будет работать, пока Вы не пропишите специальный элемент SafeControl в web.config приложения. Например так:

    <SafeControl Assembly="NMUPortal, Version=1.0.0.0, Culture=neutral, PublicKeyToken=e1ffca03d8df0be6" Namespace="NMUPortal" TypeName="*" Safe="True" />

    Естественно stsadm умеет все это делать за нас. Для этого добавим ссылку на сборку в manifest.xml:

    <Assemblies>
        <Assembly Location="NMUPortal.dll" DeploymentTarget="GlobalAssemblyCache">
          <SafeControls>
            <SafeControl Assembly="NMUPortal, Version=1.0.0.0, Culture=neutral, PublicKeyToken=e1ffca03d8df0be6" Namespace="NMUPortal" TypeName="*" Safe="True" />
          </SafeControls>
        </Assembly>
      </Assemblies>

    Тут мы указываем имя сборки, хранилище для развертывания (GAC или bin), а также описываем SafeControl, который будет добавлен в web.config.

    Чтобы сборка стала доступной, добавляем ее в ddf.-файл

    Solution\NMUPortal.dll NMUPortal.DLL

    (В настройках проекта правильно укажите директорию для сборки - Solution)

    Следующим шагом будет описание Feature, которая разворачивает веб-часть.

    Создадим Feature под названием HelloWebPart.

    <?xml version="1.0" encoding="utf-8" ?>
    <Feature Title="Hello World" Id="9194FEA3-A020-4a10-9F96-5D8FFA695E58" Scope="Site" xmlns="http://schemas.microsoft.com/sharepoint/">
      <ElementManifests>
        <ElementManifest Location="Elements.xml" />
      </ElementManifests>
    </Feature>

    Задача этой Feature разместить описание веб-части в списке веб-частей создаваемого сайта. Описание веб-части принято размещать в специальном файле с расширением .webpart. Вот этот файл:

    HeloWebPart.webpart

    <?xml version="1.0" encoding="utf-8"?>
    <WebPart xmlns="http://schemas.microsoft.com/WebPart/v2">
      <Assembly>NMUPortal, Version=1.0.0.0, Culture=neutral, PublicKeyToken=e1ffca03d8df0be6</Assembly>
      <TypeName>NMUPortal.HelloWebPart</TypeName>
      <Title>Hello</Title>
      <Description>Show Hello</Description>
      <PartImageLarge>/_layouts/images/msimagel.gif</PartImageLarge>
    </WebPart>

    Как видно, основными элементами тут, есть ссылка на сборку и имя класса, описывающего веб-часть (в одной сборке их может быть множество). Кроме того, тут есть заголовок и описание веб-части, которое отображается пользователю. Чтобы развернуть этот файл в список веб-частей сайта, опишем elements.xml следующим образом:

    <?xml version="1.0" encoding="utf-8" ?>
    <Elements xmlns="http://schemas.microsoft.com/sharepoint/">
      <Module Name="HelloWebPart" Path="dwp" List="113" Url="_catalogs/wp" RootWebOnly="TRUE">
        <File Url="HelloWebPart.webpart" Type="GhostableInLibrary">
          <Property Name="Group" Value="NMU Web Parts"/>
        </File>
      </Module>
    </Elements>

    Тут мы использовали элемент Module, который позволяет развернуть наш .webpart-файл в список веб-частей сайта (список всегда имеет ID 113).

    Чтобы развернуть Feature, добавим ее описание в manifest.xml

    <FeatureManifest Location="HelloWebPart\feature.xml"/>

    Чтобы .webpart-файл стал доступен в директории нашей Feature, добавим ссылку на него в раздел TemplateFiles:

    <TemplateFile Location="Features\HelloWebPart\dwp\HelloWebPart.webpart"/>

    Вместо TemplateFile мы могли бы использовать ElementFile при описании элементов Feature. Но я сделал почему-то так.

    Чтобы наша веб-часть была активна, опишем ссылку на Feature в onet.xml:

    <SiteFeatures>
      <Feature ID="9194FEA3-A020-4a10-9F96-5D8FFA695E58" />
    </SiteFeatures>

    И на последнем этапе внесем изменения в ddf.

    Template\Features\HelloWebPart\feature.xml HelloWebPart\feature.xml
    Template\Features\HelloWebPart\elements.xml HelloWebPart\elements.xml
    Template\Features\HelloWebPart\HelloWebPart.webpart Features\HelloWebPart\dwp\HelloWebPart.webpart

    Вот и все. Веб-часть будет доступна на сайте, созданном на основе нашего шаблона.

  • Windows SharePoint Services 3.0: Создание и развертывание решений (часть 3)

    Продолжим наращивание решения, которое мы создали в предыдущей части. Попробуем добавить в наш пустой шаблон Feature (фичу).

    Feature - специальный механизм в WSS 3.0, позволяющий определять и развертывать новые элементы, такие как веб-части, модули, меню, определение списков и др. Сам по себе мезанизм Features очень удобен, так как позволяет сконцентироваться на разработке отдельных небольших элементов, которые могут быть использованы как на готовом сайте, так и в новом шаблоне сайта. Если Вам нужно что-то изменить в существующем сайте, первым делом подумайте о Feature. Чтобы научится работать с этим механизмом нужно обратиться к разделу "HowTo:..." в SDK и посмотреть на различные варианты использования этого механизма. Очень часто приходится видеть, как разработчики создают не гибкие решения или вовсе изобретают колесо, не умея работать с Features.  Пример: Вы хотите заменить SearchBox на свою реализацию. Неправильный вариант решения: создать новую эталонную страницу или изменить существующую (что еще хуже). Правильный вариант: использовать Feature, позволяющую заменять элементы управления, объявленные как DelegateControl. Такую Feature можно развернуть в любой момент, как и деактивировать.

    Разработаем простую Feature, которая позволит добавить новый пункт меню в Site Actions.

    Создадим файл feature.xml со следующим содержимым:

    <Feature xmlns="http://schemas.microsoft.com/sharepoint/"
             Id="91E266BE-CAFB-4197-B920-31E6627C2056"
             Title="Add News"
             Description="Add news to the main page"
             Scope="Web"
             Hidden="FALSE"
             ImageUrl="menuprofile.gif">
      <ElementManifests>
        <ElementManifest Location="elements.xml"/>
      </ElementManifests>
    </Feature>

    Этот файл описывает уникальный идентификатор для Feature, ее имя, область видимости, а также ссылки на файлы с описанием элементов.

    Одним из элементов, допустимым в описании Feature является CustomAction. С помощью этого элемента можно добавить пункт в меню Site Actions. Вот как выглядит описание этого элемента в файле elements.xml:

    <Elements xmlns="http://schemas.microsoft.com/sharepoint/">
      <CustomAction Id="AddNewsAction"
                    GroupId="SiteActions"
                    Location="Microsoft.SharePoint.StandardMenu"
                    Sequence="100"
                    Title="Add News"
                    Description="Manage news"
                    ImageUrl="_layouts/images/menuprofile.gif">
        <UrlAction Url="http://www.microsoft.com"/>
      </CustomAction>
    </Elements>

    Чтобы развернуть Feature необходимо задекларировать ссылку на нее в manifest.xml. Это можно сделать с помощью элемента FeatureManifest:

    <FeatureManifests>
      <FeatureManifest Location="AddNewsSiteAction\feature.xml"/>
    </FeatureManifests>

      Нужно отметить, что развертывание Feature не активирует эту фичу на сайте, созданном по нашему шаблону. Один из способов активировать вичу, разместить ссулку на нее в файле onet.xml. Тут мы можем использовать WebFeatures или SiteFeatures элементы (в зависимости от области видимости самой Feature).

    <WebFeatures>
      <Feature ID="91E266BE-CAFB-4197-B920-31E6627C2056"></Feature>
    </WebFeatures>

    На последнем этапе расширим .ddf файл новыми элементами. При этом все элементы Feature мы пакуем в папки с соответствующим именем не добавляя имя корневой директории \Features:

    Template\Features\AddNewsSiteAction\feature.xml AddNewsSiteAction\feature.xml
    Template\Features\AddNewsSiteAction\elements.xml AddNewsSiteAction\elements.xml

    Аналогичным образом можно добавлять и активировать другие Features в наш шаблон. Отдельно стоит выделить веб-части, так как они имеют немного более сложную схему развертывания.

  • Windows SharePoint Services 3.0: Создание и развертывание решений (часть 2)

    В прошлом разделе мы создали простой шаблон. Несмотря на свою простоту, необходимо выполнить ряд действий, чтобы этот шаблон заработал для WSS. Между тем, разрабатывая собственное решение для WSS, программист сталкивается с такими элементами как features, Web-части, списки и др. Каждый из этих элементов имеет собственную структуру и механизм развертывания, что значительно усложняет процесс. Поэтому, если вы хотите создать тиражируемое решение, то должны использовать специальный механизм, позволяющий просто и эффективно доставить и развернуть решение для конечного пользователя. Этот механизм существует в виде так называемых "Решений для SharePoint".

    Решение для SharePoint представляет собой .cab файл, содержащий все необходимые элементы проекта, включая сборки, декларации сайта, списков, features и др. Все элементы могут быть разнесены по папкам. В то же время в корневой папке архива всегда содержится файл manifest.xml. Этот файл полностью описывает решение и служит отправной точкой для утилиты stsadm, которая позволяет разворачивать решения.

    Попробуйте скачать набор бесплатных шаблонов приложений с сайта Microsoft: http://www.microsoft.com/sharepoint/templates.mspx.  Все шаблоны имеют расширение .wsp. Переименуйте .wsp файл в .cab файл и сможете увидеть его структуру, а также получить доступ к ко всем элементам. Я рекомендую заглядывать в уже существующие шаблоны, чтобы лучше осознать структуру решения.

    Прежде чем рассматривать структуру manifest.xml подготовим проект Решения в Visual Studio. Первый шаг, который необходимо сделать, это активировать систему IntelliSense, которая позволит упростить работу с такими файлами как onet.xml, manifest.xml и др. Соответствующую схему можно найти в файле: C:\Program Files\Common Files\Microsoft Shared\ web server extensions\12\TEMPLATE\XML\wss.xsd. Поэтому, чтобы система IntelliSense заработала, добавьте в папку с:\program files\Microsoft Visual Studio 9.0\Xml\Schemas следующий XML файл (любое имя.xml):

    <SchemaCatalog xmlns="http://schemas.microsoft.com/xsd/catalog">

    <Schema href="C:\Program Files\Common Files\Microsoft Shared\ web server extensions\12\TEMPLATE\XML\wss.xsd" targetNamespace="http://schemas.microsoft.com/sharepoint/"/>

    </SchemaCatalog>

    Если при этом Visual Studio у вас был открыт, то его необходимо перезапустить.

    Запустите Visual Studio и создайте проект на основе шаблона Control Library, сформировав папки так, так показано на рисунке:

    image

    Как было сказано выше, информация, описывающая структуру Решения  находится в файле manifest.xml. Ниже представлен пример файла manifest.xml для нашего Шаблона сайта: 

    <?xml version="1.0" encoding="utf-8"?>
    <Solution SolutionId="467C7D0B-E3B0-4ea7-9F92-5DD53E61FD53" xmlns="http://schemas.microsoft.com/sharepoint/">
      <SiteDefinitionManifests>
        <SiteDefinitionManifest Location="NMUPortal">
          <WebTempFile Location="1033\xml\WebTemp.NMU.xml" />
        </SiteDefinitionManifest>
      </SiteDefinitionManifests>
    </Solution>

    Тут имеются корневой элемент Solution, содержащий в качестве атрибута уникальный ID (такой ID можно сгенерировать с помощью Visual Studio, меню Tools), элемент SiteDefinitionManifest, который задает корневую директорию для решения и webtemp*.xml.

    Фактически, чтобы создать решение для SharePoint, программисту необходимо описать все его элементы в manifest.xml, а затем упаковать все элементы в .cab файл, сохраняя структуру, описанную в manifest.xml. Полученный файл можно передавать утилите stsadm, которая базируясь на описании элементов в manifest.xml выполнить развертывание решения на сервер.

    ddf файл, позволяющий создать пакет описан ниже:

    .OPTION EXPLICIT
    .Set CabinetNameTemplate=NMUPortal.wsp
    .Set CompressionType=MSZIP
    .Set UniqueFiles="ON"
    .Set Cabinet=on
    .Set DiskDirectory1=
    Solution\manifest.xml manifest.xml
    Template\1033\XML\WebTemp.NMU.xml 1033\XML\WebTemp.NMU.xml
    Template\SiteTemplates\NMUPortal\XML\onet.xml NMUPortal\XML\onet.xml
    Template\SiteTemplates\NMUPortal\default.aspx NMUPortal\default.aspx

    Теперь остается вызвать утилиту makecab.exe и выполнить развертывание решения. Это можно сделать с помощью следующего кода (поместите его в build.cmd)

    @SET STSADM="c:\program files\common files\microsoft shared\web server extensions\12\bin\stsadm"

    makecab.exe /f Solution\package.ddf
    pause
    %stsadm% -o retractsolution -name NMUPortal.wsp -immediate
    %stsadm% -o deletesolution -name NMUPortal.wsp -override
    pause
    %stsadm% -o addsolution -filename "C:\Users\sbaydach\Documents\Visual Studio 2008\Projects\NMUPortal\NMUPortal\NMUPortal.wsp"
    %stsadm% -o deploysolution -name NMUPortal.wsp -immediate -allowGacDeployment -force
    pause

    Поскольку этот командный файл будет использоваться для регулярного тестирования нашего решения, то первым делом мы пробуем удалить существующее решение из базы, а только затем регистрируем новое.

    Замечание. Если вы считаете, что для разработки приложений для WSS нужно установить Win Server, то вы ошибаетесь. Вот ссылка на эту тему: http://community.bamboosolutions.com/blogs/bambooteamblog/archive/2008/05/21/how-to-install-windows-sharepoint-services-3-0-sp1-on-vista-x64-x86.aspx. Работает.

    По мере того, как мы будем наращивать решение, мы будем сталкиваться с другими элементами файла manifest.xml. В следующей части рассмотрим, как добавлять в Шаблон сайта features и веб-части.

More Posts Next page »

© 2009 Microsoft Corporation. All rights reserved. Terms of Use  |  Trademarks  |  Privacy Statement
Microsoft
Page view tracker