Вышла в свет новая версия EQATEC Profiler, бесплатного профайлера для .NET Compact Framework, о котором я писал ранее в этом посте.

Список новых фич:

Major new features:

  • Much easier to use, especially for full .NET apps
  • Profiler/viewer unified into one application
  • Take profiling-snapshots “live” for a running app
  • Report-folder is now fixed and you decide where it is

Lots of other features and changes, too:

  • Save/load settings in named config-files, or just have it handled automatically
  • EQATECProfilerCmd now take a dir or config-file as argument
  • Specify paths using environment-variables, including $(AppDir)
  • Report-files renamed to profiled-exename-timestamp.eqlog
  • Full drag/drop-support for report/config-files
  • Pass command-args to profiled app
  • Window title now displays report-name, if any
  • Profiler show list of all saved reports
  • Profiler-diagnostics saved to logfile, profiler.log
  • Run .Net CF apps directly, too
  • Comes with pre-compiled Bubbles-demo
  • “Most recently used”-dropdowns for apps, executables, and reports
  • Keyboard-shortcuts everywhere (except for call-graph)
  • Navigate easy backwards/forward in call-graph
  • Timing for full Windows .NET apps now have 1 millisec precision (was 16ms)
  • Small averages now shown with one decimal
  • Auto-snapshot for .NET Services and ASP NET apps
  • Prefix those pesky recursive methods with a “<“, as in “<433 ms”
  • Resize parts of window
  • Simplified UI and workflow
  • Fixed bug: Running a profiled app did not correctly set its working directory
  • Fixed bug: F# tail.call was not handled properly; it is ignored for now
  • and lots of other minor improvements

Узнать больше о самом профайлере и скачать его можно здесь.

Previous ArticleNext Article
Технический директор IT-Dimension, компании-разработчика кросс-платформенного программного обеспечения

Leave a Reply

Your email address will not be published. Required fields are marked *

Р.

Работаем с Flickr на .NET Compact Framework

Что-то все больше меня уносит в сторону ПО, работающего со всякими online-сервисами. Вон было недавно о Google Translate и Yahoo! Maps, а сегодня буду рассказывать о том как работать с сервисом хостинга фотографий Flickr на .NET Compact Framework.

Итак, предметом нашего внимания сегодня будет библиотека Flickr.NET, которую можно найти на CodePlex. Flickr.NET – это библиотека с открытым исходным кодом, есть версия для .NET Compact Framework. Вот ее мы и будем использовать.

Для начала Создаем новый проект для Smart Device на C#.

Затем распаковываем исходный код Flickr.NET в папку с созданным решением (рядом с папкой, в которой находится исходный код только что созданного проекта).

После этого добавляем проект FlickrNetCF в решение

Добавляем ссылку на проект FlickrNetCF в список зависимостей нашего приложения (с помощью Project -> Add Reference).

Ну вот, теперь можно работать.

Рисуем вот такую форму:

На форме:

  • Поле ввода запроса для поиска изображений
  • Кнопка поиска
  • Список (ListView) для просмотра информации о найденных изображениях
  • Кнопка открытия формы для просмотра полноразмерного изображения

Теперь можно заняться, собсьвенно, работой с Flickr. Для работы с сервисом в библиотеке FlickrNetCF есть класс Flickr. Для работы ему необходимы:

  • Application Key
  • Secret Key

Получить эти два ключа можно вот по этому адресу.

Создаем объект для работы с сервисом

using FlickrNet;
...
Flickr _flickr = new Flickr();
_flickr.ApiKey = "<здесь будет ваш API Key>";
_flickr.ApiSecret = "<здесь будет ваш Secret Key>";

Теперь нам неплохо бы обеспечить поиск изображений и отображение превью, а также информации об изображении. Загрузка превью – процесс длительный, поэтому обрабатывать информацию, полученную от сервиса будем в отдельном потоке:

Thread _downloadThread;
string _searchText = "";
...
private void StartDownloadingPreviws()
    {
    if (_downloadThread != null)
    {
        _downloadThread.Abort();
    }
    photoInfoListView.Items.Clear();
    photoThumbnailList.Images.Clear();
    _searchText = searchTextBox.Text;
    _downloadThread = new Thread(new ThreadStart(DownloadPhotosDelegate));
    _downloadThread.Start();
}

Для поиска изображений в классе Flickr предусмотрен метод PhotosSearchText(), который в качестве параметра принимает строку поискового запроса. Именно этот метод мы и будем использовать для получения информации о найденных фотографиях. Метод PhotosSearchText() возвращает объект класса Photos, из которого мы можем получить всю информацию о найденных изображениях (свойство PhotoCollection класса Photos возвращает массив объектов Photo, каждый из которых содержит полную информацию об одном изображении, например, название, ссылку на превью, ссылку на полноразмерное изображение и др.)

void DownloadPhotosDelegate()
{
    try
    {
        Photos _photos = _flickr.PhotosSearchText(_searchText);
        foreach (Photo photo in _photos.PhotoCollection)
        {
            MemoryStream photoStream = new MemoryStream();
            WebRequest request = HttpWebRequest.Create(photo.SquareThumbnailUrl);
            WebResponse response = request.GetResponse();
            MemoryStream stream = new MemoryStream();
            Stream responseStream = response.GetResponseStream();
            int readCount = 0;
            byte[] buffer = new byte[1024];
            while ((readCount =
                responseStream.Read(buffer, 0, buffer.Length)) > 0)
            {
                stream.Write(buffer, 0, readCount);
            }
            response.Close();

            Bitmap bmp = new Bitmap(stream);
            string originalURL = photo.LargeUrl;
            string title = photo.Title;
            Invoke(new AddItemDelegate(AddItemToList),
                new object[] { bmp, title,  originalURL})
        }
    }
    catch (ThreadAbortException)
    {
    }
    catch (Exception ex)
    {
        MessageBox.Show(ex.Message);
    }
}

Т.к. в .NET неглавный поток не может работать с элементами GUI, то добавление превью изображений в список нам необходимо делать в отдельном методе, который будет вызываться посредством Invoke():

Invoke(new AddItemDelegate(AddItemToList), new object[] { bmp, title,  originalURL});
...
private delegate void AddItemDelegate(Bitmap bmp, string name, string originalURL);

Bitmap ResizeBitmap(Bitmap b, System.Drawing.Size size)
{
    Bitmap result = new Bitmap(size.Width, size.Height);
    using (Graphics g = Graphics.FromImage((Image)result))
        g.DrawImage(b, 
            new Rectangle(0, 0, result.Width, result.Height), 
            new Rectangle(0, 0, b.Width, b.Height), 
            GraphicsUnit.Pixel);
    return result;
}

void AddItemToList(Bitmap bmp, string name, string originalURL)
{
    try
    {
        photoThumbnailList.Images.Add(bmp);
        ListViewItem newItem = new ListViewItem(name);
        newItem.ImageIndex = photoThumbnailList.Images.Count - 1;
        newItem.Tag = originalURL;
        photoInfoListView.Items.Add(newItem);
    }
    catch (Exception ex)
    {
        MessageBox.Show(ex.Message);
    }
}

ОК, с главной формой пока закончили. теперь приступим к созданию формы для просмотра полноразмерного изображения.
Добавляем в проект новую форму. На нее кладем Panel (Dock = Fill, AutoScroll = true), на Panel кладем PictureBox.
Теперь нам нужно обеспечить загрузку изображения в отдельном потоке. Подход приблизительно такой же как и в главной форме:

private Thread _downloader;
private string _imageURL = "";
Bitmap _sourceBitmap;

public string ImageURL
{
    get
    {
        return _imageURL;
    }
    set
    {
        _imageURL = value;
    }
}

public ImageViewForm()
{
    InitializeComponent();
}

void OnImageDownloaded(Bitmap bmp)
{
    _sourceBitmap = bmp;
    imageBox.Image = bmp;
    imageBox.Width = bmp.Width;
    imageBox.Height = bmp.Height;
}

private delegate void ImageDownloadedDelegate(Bitmap bmp);

void DownloadImage()
{
    try
    {
        do
        {
            if (ImageURL == null || ImageURL.Length == 0) break;
            WebRequest request = HttpWebRequest.Create(ImageURL);
            WebResponse response = request.GetResponse();
            Bitmap bmp = new Bitmap(response.GetResponseStream());
            response.Close();
            Invoke(new ImageDownloadedDelegate(OnImageDownloaded),
                new object[] { bmp });
        }
        while (false);
    }
    catch (ThreadAbortException)
    {
    }
    catch (Exception ex)
    {
        MessageBox.Show(ex.Message);
    }
}

public void StartDownloading()
{
    _downloader = new Thread(new ThreadStart(DownloadImage));
    _downloader.Start();
}

private void ImageViewForm_Closing(object sender, CancelEventArgs e)
{
    if (_downloader != null)
    {
        _downloader.Abort();
    }
}

Теперь пишем обработчик нажатия кнопки открытия формы просмотра полноразмерного изображения в главной форме

private void showPictureButton_Click(object sender, EventArgs e)
{
    do
    {
        if (photoInfoListView.SelectedIndices.Count == 0) break;
        ListViewItem item = photoInfoListView.Items[photoInfoListView.SelectedIndices[0]];
        if (item == null) break;
        string url = (item.Tag as string);
        if (url == null || url.Length == 0) break;
        ImageViewForm dlg = new ImageViewForm();
        dlg.ImageURL = url;
        dlg.StartDownloading();
        dlg.ShowDialog();
    }
    while (false);
}

Ну вот, собственно и все. В результате мы получим вот такое приложение:


Исходный код тестового приложения, а также исполняемый файл можно скачать здесь.

A.

ActionBarSherlock – Удобная реализация UI-паттерна ActionBar для Android-приложений – Часть II

Это вторая часть рассказа о библиотеке ActionBar Sherlock, реализующей UI-паттерн ActionBar для Android-приложений, а также еще ряд дополнительных возможностей. В этой части речь пойдет о том, как можно реализовать навигацию в приложении с использованием ActionBar и какие для этого есть возможности в ActionBar Sherlock.

Кастомная навигация

В ActionBar Sherlock есть возможность разместить свой View внутри ActionBar и использовать его для навигации. Так, например, можно разместить RadioGroup и при нажатии на RadioButton внутри нее переходить на нужную страницу в UI.
Пример такого XML-файла для контрола навигации ниже:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="wrap_content"
    android:layout_height="fill_parent"
    android:gravity="left|center_vertical"
    android:orientation="horizontal"
    >
    <RadioGroup
        android:id="@+id/radio_nav"
        android:orientation="horizontal"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content">
        <RadioButton
            android:id="@+id/nav_page_a"
            android:text="Page A"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"/>
        <RadioButton
            android:id="@+id/nav_page_b"
            android:text="Page B"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"/>
    </RadioGroup>
</LinearLayout>

В коде Activity необходимо вызвать метод getSupportActionBar().setCustomView() и нужній контрол будет размещен в ActionBar.

public class ActionBarSherlockTestActivity extends SherlockActivity {
	
	static final int [] pages = {
		R.layout.page_a,
		R.layout.page_b
	};
	
	private void setPage(int page_index) {
		setContentView(pages[page_index]);
	}

    @Override
    public void onCreate(Bundle savedInstanceState) {
    	setTheme(R.style.Theme_Sherlock);
        super.onCreate(savedInstanceState);
        View customNav = getLayoutInflater().inflate(R.layout.cusom_navigation, null);
        
        ((RadioGroup)customNav.findViewById(R.id.radio_nav)).setOnCheckedChangeListener(new OnCheckedChangeListener() {
            public void onCheckedChanged(RadioGroup group, int checkedId) {
            	int page_index = checkedId == R.id.nav_page_a ? 0 : 1;
            	setPage(page_index);
            }

        });
        
        getSupportActionBar().setCustomView(customNav);
        getSupportActionBar().setDisplayShowCustomEnabled(true);
        setPage(0);
    }
}

На вид получится так:

Вкладки (Tabs)

Возможность работы с вкладками сама по себе очень полезна для отображения ограниченного количества страниц в интерфейсе пользователя (напомню что “ограниченное количество страниц”, согласно UI Design Guidelines это максимум 4 страницы).
Для того, чтобы иметь возможность отображать вкладки в ActionBar нужно указать для ActionBar режим NAVIGATION_MODE_TABS. Делается это посредством метода getSupportActionBar().setNavigationMode(). Затем нужно создать нужное количество объектов Tab с помощью getSupportActionBar().newTab(), заполнить в них информацию о вкладке (например заголовок и listener) и добавить в ActionBar с помощью getSupportActionBar().addTab(tab):

public class TabNavigation extends SherlockActivity i
		mplements ActionBar.TabListener {
    
	private TextView mSelected;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        ...
        setContentView(R.layout.tab_navigation);
        mSelected = (TextView)findViewById(R.id.text);

        getSupportActionBar().setNavigationMode(
			ActionBar.NAVIGATION_MODE_TABS);
        for (int i = 1; i <= 3; i++) {
            ActionBar.Tab tab = getSupportActionBar().newTab();
            tab.setText("Tab " + i);
            tab.setTabListener(this);
            getSupportActionBar().addTab(tab);
        }
    }

    @Override
    public void onTabReselected(Tab tab, FragmentTransaction transaction) {
    }

    @Override
    public void onTabSelected(Tab tab, FragmentTransaction transaction) {
        mSelected.setText("Selected: " + tab.getText());
    }

    @Override
    public void onTabUnselected(Tab tab, FragmentTransaction transaction) {
    }
}

В listener’е можно получать уведомления о переходе на вкладку и выходе с вкладки.
По поводу же смены layout’а, то этот функционал надо реализовывать самостоятельно.
Очень полезной является возможность отображать вкладки второй строкой в ActionBar в портретном режиме и встраивать их в ActionBar в ландшафтном режиме для экономии места.

Навигация списком

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

  • Указать Navigation Mode равным NAVIGATION_MODE_LIST
  • Установить адаптер с элементами навигации и listener для получения сообщений о навигации с помощью getSupportActionBar().setListNavigationCallbacks()

В коде это будет выглядеть как-то так:

public class ListNavigation extends SherlockActivity 
		implements ActionBar.OnNavigationListener {
    private TextView mSelected;
    private String[] mLocations;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        setTheme(SampleList.THEME); //Used for theme switching in samples
        super.onCreate(savedInstanceState);

        setContentView(R.layout.list_navigation);
        mSelected = (TextView)findViewById(R.id.text);

        mLocations = getResources().getStringArray(R.array.locations);

        Context context = getSupportActionBar().getThemedContext();
        ArrayAdapter<CharSequence> list = ArrayAdapter.createFromResource(
        		context, R.array.locations, R.layout.sherlock_spinner_item);
        list.setDropDownViewResource(R.layout.sherlock_spinner_dropdown_item);

        getSupportActionBar().setNavigationMode(ActionBar.NAVIGATION_MODE_LIST);
        getSupportActionBar().setListNavigationCallbacks(list, this);
    }

    @Override
    public boolean onNavigationItemSelected(int itemPosition, long itemId) {
        mSelected.setText("Selected: " + mLocations[itemPosition]);
        return true;
    }
}

Визуально все это выглядит так:

Функционал по смене лайаута надо тоже реализовывать руками.

Кнопка возврата

Есть также возможность отображения кнопки возврата в предыдущую activity в левой части ActionBar. Для этого использается метод getSupportActionBar().setDisplayHomeAsUpEnabled(). В результате получаем такой внешний вид ActionBar:

Исходный код всех примеров можно скачать вместе с дистрибутивом библиотеки. Примеры, которые идут в поставке покрывают абсолютно все аспекты использования ActionBar Sherlock и, хотя не блещут элегантностью и богатством реализации дополнительных фич, не связанных с API самой библиотеки, позволяют понять что и как можно использовать в своем приложении.