Skip to content

Instantly share code, notes, and snippets.

@philcleveland
Last active December 28, 2015 03:29
Show Gist options
  • Save philcleveland/7435552 to your computer and use it in GitHub Desktop.
Save philcleveland/7435552 to your computer and use it in GitHub Desktop.
RxUI Search example
public partial class App : Application
{
protected override void OnStartup(StartupEventArgs e)
{
base.OnStartup(e);
//ghetto bootstrap - IS THIS OK?
var searchView = new SearchView();
searchView.ViewModel = new SearchViewModel();
searchView.DataContext = searchView.ViewModel;
var mw = new AppShell(searchView);
mw.Show();
}
}
public AppShell(SearchView searchView)
{
InitializeComponent();
this.MainPanel.Children.Add(searchView);
}
<UserControl x:Class="FeedManagement.WPF.Search.SearchView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:v="clr-namespace:FeedManagement.WPF.Search"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300">
<UserControl.Resources>
<DataTemplate DataType="{x:Type v:SearchViewModel}">
<v:SearchView ViewModel="{Binding}"/>
</DataTemplate>
</UserControl.Resources>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="1*"/>
<RowDefinition Height="8*"/>
</Grid.RowDefinitions>
<Grid Grid.Row="0">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<Label>Search</Label>
<TextBox x:Name="SearchText" Grid.Column="1"></TextBox>
<Button x:Name="SearchButton" Grid.Column="2" Content="{StaticResource Icons.Search}" ></Button>
</Grid>
<DockPanel Grid.Row="1">
<ScrollViewer VerticalScrollBarVisibility="Auto" HorizontalScrollBarVisibility="Auto">
<ListBox x:Name="TweetTiles" >
</ListBox>
</ScrollViewer>
</DockPanel>
</Grid>
</UserControl>
public partial class SearchView : UserControl , IViewFor<SearchViewModel>
{
public SearchView()
{
InitializeComponent();
//2 way bind between the view and view model of the search text
this.Bind(ViewModel, vm => vm.SearchText, v=>v.SearchText.Text);
//one way bind from the view model prop TweetTiles to the view TweetTiles.ItemsSource
this.OneWayBind(ViewModel, vm => vm.TweetTiles, v => v.TweetTiles.ItemsSource);
//binds the search command, defined on viewmodel, to the view SearchButton
this.BindCommand(ViewModel, vm => vm.SearchCommand, v => v.SearchButton);
}
//The interface implementation needs to be a DependencyProperty, so that RUI
//can pick up on view model changes. make sure that the viewmodel type matches
//the type declared in IViewFor<xxx> (e.g. is it an interface?)
public static readonly DependencyProperty ViewModelProperty =
DependencyProperty.Register("ViewModel", typeof(SearchViewModel), typeof(SearchView), new PropertyMetadata(null));
public SearchViewModel ViewModel
{
get { return (SearchViewModel)GetValue(ViewModelProperty); }
set { SetValue(ViewModelProperty, value); }
}
object IViewFor.ViewModel
{
get { return ViewModel; }
set { ViewModel = (SearchViewModel)value; }
}
}
public class SearchViewModel : ReactiveObject, ISearchViewModel
{
private Twitter _twitterAPI = new Twitter();
public ReactiveCommand SearchCommand { get; set; }
private ObservableAsPropertyHelper<List<TwitterTileViewModel>> _TweetTiles;
public List<TwitterTileViewModel> TweetTiles
{
get
{
return _TweetTiles.Value;
}
}
public SearchViewModel()
{
SearchCommand = new ReactiveCommand(null, false, null);
var results = SearchCommand.RegisterAsyncTask(t => Task.Factory.StartNew(() => _twitterAPI.SearchFor(SearchText)))
.Select(x => ConvertTweets(x));
SearchCommand.ThrownExceptions
.Subscribe(ex => this.Log().WarnException("Twitter Search Failed", ex));
_TweetTiles = new ObservableAsPropertyHelper<List<TwitterTileViewModel>>(results, vm => { this.RaisePropertyChanged("TweetTiles"); });
}
List<TwitterTileViewModel> ConvertTweets(List<Tweet> tweets)
{
var tempTiles = new List<TwitterTileViewModel>();
tweets.ForEach(tweet => tempTiles.Add(new TwitterTileViewModel(tweet)));
return new List<TwitterTileViewModel>(tempTiles);
}
private string _searchText = "";
public string SearchText
{
get { return _searchText; }
set { _searchText = value; }
}
}
System.NullReferenceException was unhandled
HResult=-2147467261
Message=Object reference not set to an instance of an object.
Source=ReactiveUI.Xaml
StackTrace:
at ReactiveUI.Xaml.ViewModelViewHost.<.ctor>b__5(<>f__AnonymousType2`2 x)
at System.Reactive.AnonymousSafeObserver`1.OnNext(T value)
at System.Reactive.Linq.Observαble.CombineLatest`3._.F.OnNext(TFirst value)
at System.Reactive.Linq.Observαble.Select`2._.OnNext(TSource value)
at System.Reactive.Linq.Observαble.DistinctUntilChanged`2._.OnNext(TSource value)
at System.Reactive.Linq.Observαble.Select`2._.OnNext(TSource value)
at System.Reactive.Linq.Observαble.Where`1._.OnNext(TSource value)
at System.Reactive.Linq.Observαble.Switch`1._.ι.OnNext(TSource value)
at System.Reactive.Linq.Observαble.Concat`1._.OnNext(TSource value)
at System.Reactive.Linq.Observαble.Select`2._.OnNext(TSource value)
at System.Reactive.AutoDetachObserver`1.OnNextCore(T value)
at System.Reactive.ObserverBase`1.OnNext(T value)
at ReactiveUI.Xaml.DependencyObjectObservableForProperty.<>c__DisplayClass4.<>c__DisplayClass6.<GetNotificationForProperty>b__2(Object o, EventArgs e)
at MS.Internal.ComponentModel.PropertyChangeTracker.OnPropertyInvalidation(DependencyObject d, DependencyPropertyChangedEventArgs args)
at System.Windows.DependentList.InvalidateDependents(DependencyObject source, DependencyPropertyChangedEventArgs sourceArgs)
at System.Windows.DependencyObject.NotifyPropertyChange(DependencyPropertyChangedEventArgs args)
at System.Windows.DependencyObject.UpdateEffectiveValue(EntryIndex entryIndex, DependencyProperty dp, PropertyMetadata metadata, EffectiveValueEntry oldEntry, EffectiveValueEntry& newEntry, Boolean coerceWithDeferredReference, Boolean coerceWithCurrentValue, OperationType operationType)
at System.Windows.StyleHelper.ApplyTemplatedParentValue(DependencyObject container, FrameworkObject child, Int32 childIndex, FrugalStructList`1& childRecordFromChildIndex, DependencyProperty dp, FrameworkElementFactory templateRoot)
at System.Windows.StyleHelper.InvalidatePropertiesOnTemplateNode(DependencyObject container, FrameworkObject child, Int32 childIndex, FrugalStructList`1& childRecordFromChildIndex, Boolean isDetach, FrameworkElementFactory templateRoot)
at System.Windows.FrameworkTemplate.InvalidatePropertiesOnTemplate(DependencyObject container, Object currentObject)
at System.Windows.FrameworkTemplate.HandleBeforeProperties(Object createdObject, DependencyObject& rootObject, DependencyObject container, FrameworkElement feContainer, INameScope nameScope)
at System.Windows.FrameworkTemplate.<>c__DisplayClass6.<LoadOptimizedTemplateContent>b__3(Object sender, XamlObjectEventArgs args)
at System.Xaml.XamlObjectWriter.OnBeforeProperties(Object value)
at System.Xaml.XamlObjectWriter.Logic_CreateAndAssignToParentStart(ObjectWriterContext ctx)
at System.Xaml.XamlObjectWriter.WriteEndObject()
at System.Xaml.XamlWriter.WriteNode(XamlReader reader)
at System.Windows.FrameworkTemplate.LoadTemplateXaml(XamlReader templateReader, XamlObjectWriter currentWriter)
at System.Windows.FrameworkTemplate.LoadTemplateXaml(XamlObjectWriter objectWriter)
at System.Windows.FrameworkTemplate.LoadOptimizedTemplateContent(DependencyObject container, IComponentConnector componentConnector, IStyleConnector styleConnector, List`1 affectedChildren, UncommonField`1 templatedNonFeChildrenField)
at System.Windows.FrameworkTemplate.LoadContent(DependencyObject container, List`1 affectedChildren)
at System.Windows.StyleHelper.ApplyTemplateContent(UncommonField`1 dataField, DependencyObject container, FrameworkElementFactory templateRoot, Int32 lastChildIndex, HybridDictionary childIndexFromChildID, FrameworkTemplate frameworkTemplate)
at System.Windows.FrameworkTemplate.ApplyTemplateContent(UncommonField`1 templateDataField, FrameworkElement container)
at System.Windows.FrameworkElement.ApplyTemplate()
at System.Windows.FrameworkElement.MeasureCore(Size availableSize)
at System.Windows.UIElement.Measure(Size availableSize)
at System.Windows.Controls.Border.MeasureOverride(Size constraint)
at System.Windows.FrameworkElement.MeasureCore(Size availableSize)
at System.Windows.UIElement.Measure(Size availableSize)
at System.Windows.Controls.Control.MeasureOverride(Size constraint)
at System.Windows.FrameworkElement.MeasureCore(Size availableSize)
at System.Windows.UIElement.Measure(Size availableSize)
at System.Windows.Controls.VirtualizingStackPanel.MeasureChild(IItemContainerGenerator& generator, IContainItemStorage& itemStorageProvider, IContainItemStorage& parentItemStorageProvider, Object& parentItem, Boolean& hasUniformOrAverageContainerSizeBeenSet, Double& computedUniformOrAverageContainerSize, Boolean& computedAreContainersUniformlySized, IList& items, Object& item, IList& children, Int32& childIndex, Boolean& visualOrderChanged, Boolean& isHorizontal, Size& childConstraint, Rect& viewport, VirtualizationCacheLength& cacheSize, VirtualizationCacheLengthUnit& cacheUnit, Boolean& foundFirstItemInViewport, Double& firstItemInViewportOffset, Size& stackPixelSize, Size& stackPixelSizeInViewport, Size& stackPixelSizeInCacheBeforeViewport, Size& stackPixelSizeInCacheAfterViewport, Size& stackLogicalSize, Size& stackLogicalSizeInViewport, Size& stackLogicalSizeInCacheBeforeViewport, Size& stackLogicalSizeInCacheAfterViewport, Boolean& mustDisableVirtualization, Boolean isBeforeFirstItem, Boolean isAfterFirstItem, Boolean isAfterLastItem, Boolean skipActualMeasure, Boolean skipGeneration, Boolean& hasBringIntoViewContainerBeenMeasured, Boolean& hasVirtualizingChildren)
at System.Windows.Controls.VirtualizingStackPanel.MeasureOverrideImpl(Size constraint, Nullable`1& lastPageSafeOffset, List`1& previouslyMeasuredOffsets, Boolean remeasure)
at System.Windows.Controls.VirtualizingStackPanel.MeasureOverride(Size constraint)
at System.Windows.FrameworkElement.MeasureCore(Size availableSize)
at System.Windows.UIElement.Measure(Size availableSize)
at System.Windows.ContextLayoutManager.UpdateLayout()
at System.Windows.ContextLayoutManager.UpdateLayoutCallback(Object arg)
at System.Windows.Media.MediaContext.InvokeOnRenderCallback.DoWork()
at System.Windows.Media.MediaContext.FireInvokeOnRenderCallbacks()
at System.Windows.Media.MediaContext.RenderMessageHandlerCore(Object resizedCompositionTarget)
at System.Windows.Media.MediaContext.AnimatedRenderMessageHandler(Object resizedCompositionTarget)
at System.Windows.Threading.ExceptionWrapper.InternalRealCall(Delegate callback, Object args, Int32 numArgs)
at MS.Internal.Threading.ExceptionFilterHelper.TryCatchWhen(Object source, Delegate method, Object args, Int32 numArgs, Delegate catchHandler)
at System.Windows.Threading.DispatcherOperation.InvokeImpl()
at System.Windows.Threading.DispatcherOperation.InvokeInSecurityContext(Object state)
at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx)
at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx)
at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state)
at System.Windows.Threading.DispatcherOperation.Invoke()
at System.Windows.Threading.Dispatcher.ProcessQueue()
at System.Windows.Threading.Dispatcher.WndProcHook(IntPtr hwnd, Int32 msg, IntPtr wParam, IntPtr lParam, Boolean& handled)
at MS.Win32.HwndWrapper.WndProc(IntPtr hwnd, Int32 msg, IntPtr wParam, IntPtr lParam, Boolean& handled)
at MS.Win32.HwndSubclass.DispatcherCallbackOperation(Object o)
at System.Windows.Threading.ExceptionWrapper.InternalRealCall(Delegate callback, Object args, Int32 numArgs)
at MS.Internal.Threading.ExceptionFilterHelper.TryCatchWhen(Object source, Delegate method, Object args, Int32 numArgs, Delegate catchHandler)
at System.Windows.Threading.Dispatcher.LegacyInvokeImpl(DispatcherPriority priority, TimeSpan timeout, Delegate method, Object args, Int32 numArgs)
at MS.Win32.HwndSubclass.SubclassWndProc(IntPtr hwnd, Int32 msg, IntPtr wParam, IntPtr lParam)
at MS.Win32.UnsafeNativeMethods.DispatchMessage(MSG& msg)
at System.Windows.Threading.Dispatcher.PushFrameImpl(DispatcherFrame frame)
at System.Windows.Threading.Dispatcher.PushFrame(DispatcherFrame frame)
at System.Windows.Threading.Dispatcher.Run()
at System.Windows.Application.RunDispatcher(Object ignore)
at System.Windows.Application.RunInternal(Window window)
at System.Windows.Application.Run(Window window)
at System.Windows.Application.Run()
at FeedManagement.WPF.App.Main() in c:\Users\phil.SONOCINE\Documents\Visual Studio 2013\Projects\FeedReaders\FeedManagement.WPF\obj\Debug\App.g.cs:line 0
at System.AppDomain._nExecuteAssembly(RuntimeAssembly assembly, String[] args)
at System.AppDomain.ExecuteAssembly(String assemblyFile, Evidence assemblySecurity, String[] args)
at Microsoft.VisualStudio.HostingProcess.HostProc.RunUsersAssembly()
at System.Threading.ThreadHelper.ThreadStart_Context(Object state)
at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx)
at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx)
at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state)
at System.Threading.ThreadHelper.ThreadStart()
InnerException:
@anaisbetts
Copy link

On line 19 of SearchViewModel, you're setting SearchText on a non-UI thread. Instead, write it this way:

var results = SearchCommand.RegisterAsyncTask(t =>Task.Factory.StartNew(() => twitterAPI.SearchFor(SearchText));
results.Subscribe(x => SearchResult = x);

Also, the property setter on line 42 should be written as:

set { this.RaiseAndSetIfChanged(ref _SearchResult, value); }

As the name implies, RaiseAndSetIfChanged only does something if the property changes. But since you updated the field yourself, the property is always equal according to RaiseAndSetIfChanged

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment