Click here to Skip to main content
15,891,864 members
Articles / Web Development / ASP.NET

Signum Framework Principles

Rate me:
Please Sign up or sign in to vote.
4.74/5 (27 votes)
25 Jul 2011CPOL18 min read 99.3K   1.1K   86  
Explains the philosophy behind Signum Framework, an ORM with a full LINQ Provider that encourages an entities-first approach.
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using Microsoft.Win32;
using Signum.Entities;
using Signum.Utilities;
using System.Windows.Input;
using System.Reflection;
using System.IO;
using System.Windows.Media.Imaging;
using Signum.Entities.DynamicQuery;
using Signum.Entities.Reflection;
using Signum.Utilities.Reflection;
using System.Windows.Media;
using Signum.Services;

namespace Signum.Windows
{
    public partial class SearchControl
    {
        private GridViewColumnHeader lastHeaderClicked = null;
        private ListSortDirection lastDirection = ListSortDirection.Ascending;

        public static readonly DependencyProperty QueryNameProperty =
            DependencyProperty.Register("QueryName", typeof(object), typeof(SearchControl), new UIPropertyMetadata(null));
        public object QueryName
        {
            get { return (object)GetValue(QueryNameProperty); }
            set { SetValue(QueryNameProperty, value); }
        }

        public static readonly DependencyProperty FilterOptionsProperty =
          DependencyProperty.Register("FilterOptions", typeof(FreezableCollection<FilterOptions>), typeof(SearchControl), new UIPropertyMetadata(null));
        public FreezableCollection<FilterOptions> FilterOptions
        {
            get { return (FreezableCollection<FilterOptions>)GetValue(FilterOptionsProperty); }
            set { SetValue(FilterOptionsProperty, value); }
        }

        public static readonly DependencyProperty SelectedItemProperty =
          DependencyProperty.Register("SelectedItem", typeof(object), typeof(SearchControl), new UIPropertyMetadata(null));
        public object SelectedItem
        {
            get { return (object)GetValue(SelectedItemProperty); }
            set { SetValue(SelectedItemProperty, value); }
        }


        public static readonly DependencyProperty SelectedItemsProperty =
          DependencyProperty.Register("SelectedItems", typeof(object[]), typeof(SearchControl), new UIPropertyMetadata(null));
        public object[] SelectedItems
        {
            get { return (object[])GetValue(SelectedItemsProperty); }
            set { SetValue(SelectedItemsProperty, value); }
        }

        public static readonly DependencyProperty MultiSelectionProperty =
            DependencyProperty.Register("MultiSelection", typeof(bool), typeof(SearchControl), new UIPropertyMetadata(true));
        public bool MultiSelection
        {
            get { return (bool)GetValue(MultiSelectionProperty); }
            set { SetValue(MultiSelectionProperty, value); }
        }

        public static readonly DependencyProperty ModeProperty =
                DependencyProperty.Register("Mode", typeof(FilterMode), typeof(SearchControl), new FrameworkPropertyMetadata(FilterMode.Hidden,
        (d, e) => ((SearchControl)d).ModeChanged()));
        public FilterMode Mode
        {
            get { return (FilterMode)GetValue(ModeProperty); }
            set { SetValue(ModeProperty, value); }
        }

        public static readonly DependencyProperty SearchOnLoadProperty =
          DependencyProperty.Register("SearchOnLoad", typeof(bool), typeof(SearchControl), new UIPropertyMetadata(true));
        public bool SearchOnLoad
        {
            get { return (bool)GetValue(SearchOnLoadProperty); }
            set { SetValue(SearchOnLoadProperty, value); }
        }

        public static readonly DependencyProperty ViewProperty =
           DependencyProperty.Register("View", typeof(bool), typeof(SearchControl), new FrameworkPropertyMetadata(true, (d, e) => ((SearchControl)d).UpdateVisibility()));
        public bool View
        {
            get { return (bool)GetValue(ViewProperty); }
            set { SetValue(ViewProperty, value); }
        }

        public static readonly DependencyProperty CreateProperty =
            DependencyProperty.Register("Create", typeof(bool), typeof(SearchControl), new FrameworkPropertyMetadata(true, (d, e) => ((SearchControl)d).UpdateVisibility()));
        public bool Create
        {
            get { return (bool)GetValue(CreateProperty); }
            set { SetValue(CreateProperty, value); }
        }

        public static readonly DependencyProperty ViewOnCreateProperty =
          DependencyProperty.Register("ViewOnCreate", typeof(bool), typeof(SearchControl), new UIPropertyMetadata(true));
        public bool ViewOnCreate
        {
            get { return (bool)GetValue(ViewOnCreateProperty); }
            set { SetValue(ViewOnCreateProperty, value); }
        }


        private static readonly DependencyPropertyKey EntityTypeKey =
         DependencyProperty.RegisterReadOnly("EntityType", typeof(Type), typeof(SearchControl), new UIPropertyMetadata(null));
        public static readonly DependencyProperty EntityTypeProperty = EntityTypeKey.DependencyProperty;
        public Type EntityType
        {
            
            get { return (Type)GetValue(EntityTypeProperty); }
        }

 
        private void UpdateVisibility()
        {
            btCreate.Visibility = Create ? Visibility.Visible : Visibility.Collapsed;
            UpdateViewSelection();
        }

        public event Func<object> Creating;
        public event Action<object> Viewing;
        public event Action DoubleClick; 

        public SearchControl()
        {
            this.InitializeComponent();

            if (!DesignerProperties.GetIsInDesignMode(this) && !(Server.ServerProxy is IQueryServer))
                throw new ApplicationException("Your current server does not implement IViewServer");

            FilterOptions = new FreezableCollection<FilterOptions>();
            this.Loaded += new RoutedEventHandler(SearchWindow_Loaded);
        }

        int entityIndex;
        QueryResult queryResult;
        public QueryResult QueryResult { get { return queryResult; } }

        public static readonly RoutedEvent QueryResultChangedEvent = EventManager.RegisterRoutedEvent(
            "QueryResultChanged", RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(SearchControl));
        public event RoutedEventHandler QueryResultChanged
        {
            add { AddHandler(QueryResultChangedEvent, value); }
            remove { RemoveHandler(QueryResultChangedEvent, value); }
        }

        void SearchWindow_Loaded(object sender, RoutedEventArgs e)
        {
            if (DesignerProperties.GetIsInDesignMode(this) || QueryName == null)
                return;

            QueryDescription view = ((IQueryServer)Server.ServerProxy).GetQueryDescription(QueryName);

            Column entity = view.Columns.SingleOrDefault(a => a.IsEntity);
            if (entity != null)
            {
                entityIndex = view.Columns.IndexOf(entity);
                SetValue(EntityTypeKey, Reflector.ExtractLazy(entity.Type));

                EntitySettings es = Navigator.NavigationManager.Settings.TryGetC(EntityType);

                if (es != null)
                {
                    if (this.NotSet(ViewProperty) && View)
                        View = es == null ? false : es.IsViewable(true);

                    if (this.NotSet(CreateProperty) && Create)
                        Create = es == null ? true : es.IsCreable(true);
                }
            }

            var columns = view.Columns.Where(a => a.Filterable).ToList();

            foreach (var fo in FilterOptions)
            {
                fo.Column = columns.Where(c => c.Name == fo.ColumnName)
                    .Single(Properties.Resources.Column0NotFoundOnQuery1.Formato(fo.ColumnName, QueryName));
                fo.RefreshRealValue();
            }

            filterBuilder.Columns = columns;
            filterBuilder.Filters = new ObservableCollection<FilterOptions>(FilterOptions);

            GenerateListViewColumns(view);

            ModeChanged();

            if (GetCustomMenuItems != null)
            {
                MenuItem[] menus = GetCustomMenuItems(QueryName);
                foreach (MenuItem mi in menus)
                {
                    menu.Items.Add(mi);
                }
            }

            if (SearchOnLoad)
                Search();

        }

        private void UpdateViewSelection()
        {
            btView.Visibility = lvResult.SelectedItem != null && View ? Visibility.Visible : Visibility.Collapsed;

            SelectedItem = ((object[])lvResult.SelectedItem).TryCC(a => a[entityIndex]);
            if (MultiSelection)
                SelectedItems = lvResult.SelectedItems.Cast<object[]>().Select(a => a[entityIndex]).ToArray();
            else
                SelectedItems = null;
        }

        private void GenerateListViewColumns(QueryDescription view)
        {
            gvResults.Columns.Clear();

            view.Columns.ForEach((c, i) =>
            {
                if (c.IsEntity) return;

                Binding b = new Binding("[{0}]".Formato(i)) { Mode = BindingMode.OneTime };
                DataTemplate dt = CreateDataTemplate(c.Type, b);
                gvResults.Columns.Add(
                new GridViewColumn
                {
                    Header = c.DisplayName,
                    DisplayMemberBinding = dt == null ? b : null,
                     
                    CellTemplate = dt,
                });
            });
        }

        private DataTemplate CreateDataTemplate(Type type, Binding b)
        {
            if (IsNumberOrDate(type))
                return new DataTemplate
                {
                    VisualTree = new FrameworkElementFactory(typeof(TextBlock))
                            .Do(f => f.SetValue(TextBlock.TextAlignmentProperty, TextAlignment.Right))
                            .Do(f => f.SetBinding(TextBlock.TextProperty, b))
     
                };

            if (!typeof(IdentifiableEntity).IsAssignableFrom(type) &&
                !typeof(Lazy).IsAssignableFrom(type))
                return null;

            if (!Navigator.FindSettings(type).TryCS(a => a.IsViewable(false)) ?? false)
                return null;

            return new DataTemplate(type)
            {
                VisualTree = new FrameworkElementFactory(typeof(LightEntityLine))
                    .Do(f => f.SetBinding(LightEntityLine.EntityProperty, b))
                    .Do(f => f.SetValue(LightEntityLine.HorizontalAlignmentProperty, HorizontalAlignment.Left))
            };
        }

        private bool IsNumberOrDate(Type type)
        {
            type = type.UnNullify();
            if (type.IsEnum)
                return false;

            switch (Type.GetTypeCode(type))
            {
                case TypeCode.Byte:
                case TypeCode.DateTime:
                case TypeCode.Decimal:
                case TypeCode.Double:
                case TypeCode.Int16:
                case TypeCode.Int32:
                case TypeCode.Int64:
                case TypeCode.SByte:
                case TypeCode.Single:
                case TypeCode.UInt16:
                case TypeCode.UInt32:
                case TypeCode.UInt64:
                    return true; 
            }
            return false; 
        }

        private void FilterBuilder_SearchClicked(object sender, RoutedEventArgs e)
        {
            Search();
        }

        public List<Filter> CurrentFilters()
        {
            return filterBuilder.Filters.Select(f => f.ToFilter()).ToList();
        }

        public void Search()
        {
            btFind.IsEnabled = false;

            queryResult = null;

            RaiseEvent(new RoutedEventArgs(QueryResultChangedEvent));

            object vn = QueryName;
            var lf = CurrentFilters(); 
            tbResultados.Visibility = Visibility.Hidden;

            int? limit = tbLimite.Text.ToInt(); 

            Async.Do(this.FindCurrentWindow(),
                () => queryResult = ((IQueryServer)Server.ServerProxy).GetQueryResult(vn, lf, limit),
                () => 
                { 
                    lvResult.ItemsSource = queryResult.Data;
                    if (queryResult.Data.Length > 0)
                    {
                        lvResult.SelectedIndex = 0;
                        lvResult.ScrollIntoView(queryResult.Data.First());
                    }
                    lvResult.Focus();
                    tbResultados.Visibility = Visibility.Visible;
                    tbResultados.Foreground = queryResult.Data.Length == limit ? Brushes.Red : Brushes.Black;
                    RaiseEvent(new RoutedEventArgs(QueryResultChangedEvent));
                },
                () => { btFind.IsEnabled = true; });
        }

        private void btView_Click(object sender, RoutedEventArgs e)
        {
            OnViewClicked();
        }

        private void OnViewClicked()
        {
            object[] row = (object[])lvResult.SelectedItem;

            if (row == null)
                return;

            object entity = row[entityIndex];

            OnViewing(entity);
        }

        private void btCreate_Click(object sender, RoutedEventArgs e)
        {
            OnCreate();
        }

        protected void OnCreate()
        {
            if (!Create)
                return;

            object result = Creating == null ? Constructor.Construct(EntityType, this.FindCurrentWindow()) : Creating();

            if (result == null)
                return;

            if (ViewOnCreate)
            {
                OnViewing(result);
            }
        }

        protected void OnViewing(object entity)
        {
            if (!View)
                return;

            if (this.Viewing == null)
                Navigator.View(new ViewOptions { Buttons = ViewButtons.SaveClose, Admin = true }, entity);
            else
                this.Viewing(entity);
        }


        private void lvResult_SelectionChanged(object sender, SelectionChangedEventArgs e)
        {
            UpdateViewSelection();
        }

      
        private void expander_Expanded(object sender, RoutedEventArgs e)
        {
            if (!ignoreExpanded)
            {
                Mode = expander.IsExpanded ? (expander.IsEnabled ? FilterMode.Visible : FilterMode.VisibleAndReadOnly) :
                       (expander.IsEnabled ? FilterMode.Visible : FilterMode.VisibleAndReadOnly);
            }
        }

        bool ignoreExpanded = false;

        private void ModeChanged()
        {
            expander.IsEnabled = Mode == FilterMode.Hidden || Mode == FilterMode.Visible;
            try
            {
                ignoreExpanded = true;
                expander.IsExpanded = Mode == FilterMode.VisibleAndReadOnly || Mode == FilterMode.Visible;
            }
            finally
            {
                ignoreExpanded = false;
            }
        }

        private void lvResult_MouseDoubleClick(object sender, System.Windows.Input.MouseButtonEventArgs e)
        {
            if (DoubleClick != null)
                DoubleClick();
            else
                OnViewClicked();
            e.Handled = true;
        }

        void GridViewColumnHeaderClickedHandler(object sender, RoutedEventArgs e)
        {
            if (queryResult == null || queryResult.Data == null)
                return;

            GridViewColumnHeader headerClicked = e.OriginalSource as GridViewColumnHeader;
            if (headerClicked == null || headerClicked.Role == GridViewColumnHeaderRole.Padding)
                return;

            ListSortDirection direction;
            if (headerClicked != lastHeaderClicked)
                direction = ListSortDirection.Ascending;
            else
            {
                if (lastDirection == ListSortDirection.Ascending)
                    direction = ListSortDirection.Descending;
                else
                    direction = ListSortDirection.Ascending;
            }

            string header = headerClicked.Column.Header as string;
            int colIndex = queryResult.Columns.IndexOf(d => d.DisplayName == header);

            btFind.IsEnabled = false;

            Async.Do(this.FindCurrentWindow(),
                   () => queryResult.Data = SortResults(colIndex, direction),
                   () => { lvResult.ItemsSource = queryResult.Data; lvResult.SelectedIndex = 0; lvResult.Focus(); },
                   () => { btFind.IsEnabled = true; });

            lastHeaderClicked = headerClicked;
            lastDirection = direction;
        }

        private object[][] SortResults(int colIndex, ListSortDirection direccion)
        {
            Type type = queryResult.Columns[colIndex].Type;

            Func<object[], IComparable> lambda = null;

            if (typeof(IComparable).IsAssignableFrom(type.UnNullify()))
                lambda = arr => (IComparable)arr[colIndex];
            else
                lambda = arr => arr[colIndex].TryCC(a => a.ToString());

            if (direccion == ListSortDirection.Ascending)
                return queryResult.Data.OrderBy(lambda).ToArray();
            else
                return queryResult.Data.OrderByDescending(lambda).ToArray();
        }


        public static event Func<object, MenuItem[]> GetCustomMenuItems;
    }

    public class SearchControlMenuItem : MenuItem
    {
        protected SearchControl SearchControl; 

        protected override void OnInitialized(EventArgs e)
        {
            this.Loaded += new RoutedEventHandler(SearchControlMenuItem_Loaded);
            base.OnInitialized(e);
        }

        void SearchControlMenuItem_Loaded(object sender, RoutedEventArgs e)
        {
            SearchControl = this.Parents().OfType<SearchControl>().First();

            SearchControl.QueryResultChanged += new RoutedEventHandler(searchControl_QueryResultChanged);

            Initialize(); 
        }

        void searchControl_QueryResultChanged(object sender, RoutedEventArgs e)
        {
            QueryResultChanged(); 
        }

        protected virtual void Initialize()
        {

        }

        protected virtual void QueryResultChanged()
        {
            
        }
    }
}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer (Senior) Signum Software
Spain Spain
I'm Computer Scientist, one of the founders of Signum Software, and the lead developer behind Signum Framework.

www.signumframework.com

I love programming in C#, Linq, Compilers, Algorithms, Functional Programming, Computer Graphics, Maths...

Comments and Discussions