Skip to content

Commit

Permalink
Add searching support to MonoTouch.Dialog and add more docs
Browse files Browse the repository at this point in the history
  • Loading branch information
Miguel de Icaza authored and Miguel de Icaza committed May 16, 2010
1 parent 8a62aa8 commit 3eb297f
Show file tree
Hide file tree
Showing 3 changed files with 226 additions and 29 deletions.
122 changes: 120 additions & 2 deletions MonoTouch.Dialog/DialogViewController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@
using System;
using MonoTouch.UIKit;
using System.Drawing;
using System.Collections.Generic;
using MonoTouch.Foundation;

namespace MonoTouch.Dialog
{
Expand Down Expand Up @@ -60,6 +62,21 @@ public event EventHandler RefreshRequested {
}
}

// If the value is 1, we are enabled, used in the source for quick computation
bool enableSearch;
public bool EnableSearch {
get {
return enableSearch;
}
set {
// After MonoTouch 3.0, we can allow for the search to be enabled/disable
if (tableView != null)
throw new ArgumentException ("You should set EnableSearch before the controller is shown");
enableSearch = value;
}
}
public string SearchPlaceholder { get; set; }

/// <summary>
/// Invoke this method to trigger a data refresh.
/// </summary>
Expand Down Expand Up @@ -122,6 +139,90 @@ public override bool ShouldAutorotateToInterfaceOrientation (UIInterfaceOrientat
return Autorotate;
}

Section [] originalSections;
Element [][] originalElements;
void StartSearch ()
{
originalSections = Root.Sections.ToArray ();
originalElements = new Element [originalSections.Length][];
for (int i = 0; i < originalSections.Length; i++)
originalElements [i] = originalSections [i].Elements.ToArray ();
}

void FinishSearch ()
{
Root.Sections = new List<Section> (originalSections);
originalSections = null;
originalElements = null;
}

void PerformFilter (string text)
{
if (text == ""){
Root.Sections = new List<Section> (originalSections);
//ReloadData ();
return;
}
bool changed = false;
var newSections = new List<Section> ();

for (int sidx = 0; sidx < originalSections.Length; sidx++){
Section newSection = null;
var section = originalSections [sidx];
bool sectionAdded = false;
Element [] elements = originalElements [sidx];

for (int eidx = 0; eidx < elements.Length; eidx++){
if (elements [eidx].Matches (text)){
if (!sectionAdded){
newSection = new Section (section.Header, section.Footer){
FooterView = section.FooterView,
HeaderView = section.HeaderView
};
newSections.Add (newSection);
}
newSection.Add (elements [eidx]);
}
}
}

Root.Sections = newSections;

ReloadData ();
}

class SearchDelegate : UISearchBarDelegate {
DialogViewController container;

public SearchDelegate (DialogViewController container)
{
this.container = container;
}

public override void OnEditingStarted (UISearchBar searchBar)
{
searchBar.ShowsCancelButton = true;
container.StartSearch ();
}

public override void OnEditingStopped (UISearchBar searchBar)
{
searchBar.ShowsCancelButton = false;
container.FinishSearch ();
}

public override void TextChanged (UISearchBar searchBar, string searchText)
{
container.PerformFilter (searchText ?? "");
}

public override void CancelButtonClicked (UISearchBar searchBar)
{
searchBar.ShowsCancelButton = false;
container.FinishSearch ();
}
}

class Source : UITableViewSource {
const float yboundary = 65;
protected DialogViewController container;
Expand Down Expand Up @@ -159,7 +260,7 @@ public override string TitleForFooter (UITableView tableView, int section)

public override UITableViewCell GetCell (UITableView tableView, MonoTouch.Foundation.NSIndexPath indexPath)
{
var section = root.Sections [indexPath.Section];
var section = root.Sections [indexPath.Section];
var element = section.Elements [indexPath.Row];

return element.GetCell (tableView);
Expand Down Expand Up @@ -240,7 +341,7 @@ public override void DraggingEnded (UIScrollView scrollView, bool willDecelerate
}
#endregion
}

//
// Performance trick, if we expose GetHeightForRow, the UITableView will
// probe *every* row for its size; Avoid this by creating a separate
Expand Down Expand Up @@ -295,6 +396,21 @@ public void DeactivateController (bool animated)
else
DismissModalViewControllerAnimated (animated);
}

void SetupSearch ()
{
if (enableSearch){
var searchBar = new UISearchBar (new RectangleF (0, 0, tableView.Bounds.Width, 44)) {
Delegate = new SearchDelegate (this)
};
if (SearchPlaceholder != null)
searchBar.Placeholder = this.SearchPlaceholder;
tableView.TableHeaderView = searchBar;
} else {
// Does not work with current Monotouch, will work with 3.0
// tableView.TableHeaderView = null;
}
}

public override void LoadView ()
{
Expand All @@ -305,6 +421,8 @@ public override void LoadView ()

UpdateSource ();
View = tableView;
SetupSearch ();

if (root == null)
return;

Expand Down
22 changes: 21 additions & 1 deletion MonoTouch.Dialog/Elements.cs
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,16 @@ public NSIndexPath IndexPath {
return null;
}
}

/// <summary>
/// Method invoked to determine if the cell matches the given text, never invoked with a null value or an empty string.
/// </summary>
public virtual bool Matches (string text)
{
if (Caption == null)
return false;
return Caption.IndexOf (text, StringComparison.CurrentCultureIgnoreCase) != -1;
}
}

public abstract class BoolElement : Element {
Expand Down Expand Up @@ -512,6 +522,11 @@ public override void Selected (DialogViewController dvc, UITableView tableView,
Tapped ();
tableView.DeselectRow (indexPath, true);
}

public override bool Matches (string text)
{
return (Value != null ? Value.IndexOf (text, StringComparison.CurrentCultureIgnoreCase) != -1: false) || base.Matches (text);
}
}

/// <summary>
Expand Down Expand Up @@ -1023,6 +1038,11 @@ protected override void Dispose (bool disposing)
entry = null;
}
}

public override bool Matches (string text)
{
return (Value != null ? Value.IndexOf (text, StringComparison.CurrentCultureIgnoreCase) != -1: false) || base.Matches (text);
}
}

public class DateTimeElement : StringElement {
Expand Down Expand Up @@ -1305,7 +1325,7 @@ public Section (UIView header, UIView footer) : base (null)
HeaderView = header;
FooterView = footer;
}

/// <summary>
/// The section header, as a string
/// </summary>
Expand Down
111 changes: 85 additions & 26 deletions README.markdown
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,11 @@ MonoTouch.Dialog

MonoTouch.Dialog is a foundation to create dialog boxes and show
table-based information without having to write dozens of delegates
and controllers for the user interface. Currently this supports
creating Dialogs based on navigation controllers that support:
and controllers for the user interface. Table support Pull-to-Refresh
as well as built-in searching.

Currently this supports creating Dialogs based on navigation controllers
that support:

* On/Off controls
* Slider (floats)
Expand Down Expand Up @@ -81,6 +84,42 @@ in the DialogViewController. Setting this value will propagate to
the various components that are shiped with MonoTouch.Dialog like the
WebView and the date and time pickers

Pull to Refresh Support
-----------------------

Pull to Refresh is a visual effect originally found in Tweetie2 which
became a popular effect among many applications.

To add automatic pull-to-refersh support to your dialogs, you only
need to do two things: hook up an event handler to be notified when
the user pulls the data and notify the DialogViewController when the
data has been loaded to go back to its default state.

Hooking up a notification is simple, just connect to the
RefreshRequested event on the DialogViewController, like this:

dvc.RefreshRequested += OnUserRequestedRefresh;

Then on your method OnUserRequestedRefresh, you would queue some data
loading, request some data from the net, or spin a thread to compute
the data. Once the data has been loaded, you must notify the
DialogViewController that the new data is in, and to restore the view
to its default state, you do this by calling ReloadComplete:

dvc.ReloadComplete ();

Search Support
--------------

To support searching, set the EnableSearch property on your
DialogViewController. You can also set the SearchPlaceholder
property to use as the watermark text in the search bar.

Searching will change the contents of the view as the user types,
it searches the visible fields and shows those to the user. The
system is extensible, so you can alter this behavior if you want,
details are below.

Samples Included
----------------

Expand All @@ -100,7 +139,11 @@ parameters:

* The object that will be edited.

* The title for the page to be rendered.
var searchBar = new UISearchBar (new RectangleF (0, 0, tableView.Bounds.Width, 44)) {
Delegate = new SearchDelegate (this)
};
if (SearchPlaceholder != null)
* The title for the page to be rendered.

A very simple dialog that contains a checkbox is shown here:

Expand Down Expand Up @@ -395,11 +438,43 @@ structure created by Sections() and Elements() are merely calls to
either RootElement.Add () or Section.Add() that the C# compiler
invokes for us.

The basic principle is that the DialogViewController shows one
RootElement, and a RootElement is made up of Sections which in turn
can contain any kind of Element (including other RootElements).

RootElements inside a Section when tapped have the effect of activating
a nested UI on a new DialogViewController.

The hierarchy of Elements looks like this:

Element
BadgeElement
BoolElement
BooleanElement - uses an on/off slider
BooleanImageElement - uses images for true/false
EntryElement
FloatElement
HtmlElement
ImageElement
MultilineElement
RootElement (container for Sections)
Section (only valid container for Elements)
StringElement
CheckboxElement
DateTimeElement
DateElement
TimeElement
ImageStringElement
RadioElement
StyleStringElement
UIViewElement
Additionally notice that when adding elements to a section, you
can use either Elements or UIViews directly. The UIViews are
just wrapped in a special UIViewElement element.

In addition
You can also create your own Elements by subclassing one of the
above elements and overriding a handful of methods.

RootElement
-----------
Expand Down Expand Up @@ -611,30 +686,14 @@ methods:
// To detect when the user has tapped on the cell
void Selected (DialogViewController dvc, UITableView tableView, NSIndexPath path)

Pull to Refresh Support
=======================

Pull to Refresh is a visual effect originally found in Tweetie2 which
became a popular effect among many applications.

To add automatic pull-to-refersh support to your dialogs, you only
need to do two things: hook up an event handler to be notified when
the user pulls the data and notify the DialogViewController when the
data has been loaded to go back to its default state.

Hooking up a notification is simple, just connect to the
RefreshRequested event on the DialogViewController, like this:

dvc.RefreshRequested += OnUserRequestedRefresh;

Then on your method OnUserRequestedRefresh, you would queue some data
loading, request some data from the net, or spin a thread to compute
the data. Once the data has been loaded, you must notify the
DialogViewController that the new data is in, and to restore the view
to its default state, you do this by calling ReloadComplete:
// If you support search, to probe if the cell matches the user input
bool Matches (string text)

dvc.ReloadComplete ();
If your element can have a variable size, you need to implement the
IElementSizing interface, which contains one method:

// Returns the height for the cell at indexPath.Section, indexPath.Row
float GetHeight (UITableView tableView, NSIndexPath indexPath);

Customizing the DialogViewController
====================================
Expand Down

0 comments on commit 3eb297f

Please sign in to comment.