2017/11/14 - [프로그램 자료/Visual C#] - 유저콘트롤 사용시 속성 정의 UserControl




얼마전에 이것저것 속성 정의할 일이 있어서 사용해봤던 propertygrid 콘트롤을 정리해본다. 


일단 소스부터. 

아래 내용은 속성을 정의했던 클래스이며, 생성자는 빠져있다.

생성자에 별 내용 없다. 


유심히 봐야하는 곳은 타입 컨버터.


[TypeConverter(typeof(PropertySorter))]

public class HtmlTag

{

 

    public void GetDataFieldList()

    {

        Dictionary<string, string> rtnList = new Dictionary<string, string>();

        if (this.InputType == "int")

        {

            for (int i = 301; i < 396; i++)

                rtnList.Add(string.Format("{0}({1})", string.Format("c{0:000}", i).ToUpper(), "int"), string.Format("c{0:000}", i));

 

        }

        else

        {

            for (int i = 396; i < 426; i++)

                rtnList.Add(string.Format("{0}({1})", string.Format("c{0:000}", i).ToUpper(), "bigint"), string.Format("c{0:000}", i));

        }

        DataFieldConverter.List = rtnList;

    }

 

    [CategoryAttribute("\t\t\t\t\t\t\t\t\t\t객체속성"),

    DisplayName("객체종류"),

    PropertyOrder(1),

    DescriptionAttribute("그룹박스, 텍스트박스, 체크, 콤보 등")]

    [TypeConverter(typeof(TagConverter))]

    public string PropTag

    {

        get

        {

            string val = string.Empty;

            //tag, inputtype 가져와서 조합

            if (string.IsNullOrEmpty(this.InputType))

                val = this.TAG;

            else

                val = this.InputType;

            return TagConverter.List.FindKeyByValue<string, string>(val) as string;

        }

        set

        {

            string iValue = TagConverter.List[value];

 

            if (inputList.Contains(iValue))

            {

                this.TAG = "input";

                this.InputType = iValue;

            }

            else

            {

                this.TAG = iValue;

                this.InputType = null;

            }

            //datafield 콤보박스 새로 생성하여 넘김

            this.DataField = null;

            GetDataFieldList();

        }

    }

 

    [Browsable(false)]

    public string TAG { get; set; }

 

    [Browsable(false)]

    public string InputType { get; set; }

 

 

    [CategoryAttribute("\t\t\t\t\t\t\t\t데이터 저장"),

    DisplayName("저장칼럼"),

    PropertyOrder(1),

    DescriptionAttribute("TNP_DATA의 저장될 Column")]

    [TypeConverter(typeof(DataFieldConverter))]

    public string DataFieldDisplay

    {

        get

        {

            return DataFieldConverter.List.FindKeyByValue<string, string>(DataField) as string;

        }

        set

        {

            this.DataField = DataFieldConverter.List[value];

        }

    }

 

    [Browsable(false)]

    public string DataField { get; set; }

 

 

}

 

/// <summary>

/// tag converter

/// </summary>

public class TagConverter : TypeConverter

{

    public static Dictionary<string, string> List = new Dictionary<string, string>

    {

        {"그룹박스", "div" }

        ,{"문자열(한줄)", "text" }

        ,{"정수(Int)", "int" }

        ,{"날짜", "date" }

        ,{"문자열(여러줄)", "textarea" }

        ,{"정수(Long)", "bigint" }

        ,{"실수(Numeric)", "numeric" }

        ,{"체크박스", "check" }

        ,{"콤보박스", "select" }

        ,{"이미지", "img" }

    };

 

    public override bool GetStandardValuesSupported(ITypeDescriptorContext context)

    {

        return true;

    }

    public override bool GetStandardValuesExclusive(ITypeDescriptorContext context)

    {

        return true;

    }

    public override System.ComponentModel.TypeConverter.StandardValuesCollection GetStandardValues(ITypeDescriptorContext context)

    {

        return new StandardValuesCollection(new List<string>(List.Keys).ToArray());

    }

}

 

/// <summary>

/// DataField converter

/// </summary>

public class DataFieldConverter : TypeConverter

{

    public static Dictionary<string, string> List = new Dictionary<string, string>();

 

    public override bool GetStandardValuesSupported(ITypeDescriptorContext context)

    {

        return true;

    }

    public override bool GetStandardValuesExclusive(ITypeDescriptorContext context)

    {

        return true;

    }

    public override System.ComponentModel.TypeConverter.StandardValuesCollection GetStandardValues(ITypeDescriptorContext context)

    {

        return new StandardValuesCollection(new List<string>(List.Keys).ToArray());

    }

}




중간에 dict에서 값으로 key 를 찾는 부분이 있는데, 아래와 같이 함수를 쓰면된다.

linq 사용해서 찾으면 한줄이면 되는데,  C# 2.0 버전이라 그냥 foreach로 돌린다.


 

public static object FindKeyByValue<Tk, Tv>(this Dictionary<Tk, Tv> dictionary, Tv value)

{

    object rtn = null;

    foreach (KeyValuePair<Tk, Tv> kv in dictionary)

    {

        if (kv.Value.Equals(value))

        {

            rtn = kv.Key;

            break;

        }

    }

    return rtn;

}


아래는 propertyGrid의 help? description  area 높이 조절부분


private static void ChangeDescriptionHeight(PropertyGrid grid, int height)

{

    if (grid == null) throw new ArgumentNullException("grid");

 

    foreach (Control control in grid.Controls)

        if (control.GetType().Name == "DocComment")

        {

            FieldInfo fieldInfo = control.GetType().BaseType.GetField("userSized",

                BindingFlags.Instance |

                BindingFlags.NonPublic);

            fieldInfo.SetValue(control, true);

            control.Height = height;

            return;

        }

}




다음 소스는 데이터의 무결성을 체크하면서 썼던 이벤트인데, 원래 여기에 사용하면 안되고 다른 곳에 valid 해주는 곳이 있을 것 같다.

우선 급한대로 작성한 걸 보자.


해당 propertygrid는 트리뷰랑 엮여있다. 


 

private void propertyGrid1_PropertyValueChanged(object s, PropertyValueChangedEventArgs e)

{

 

    HtmlTag tag = this.propertyGrid1.SelectedObject as HtmlTag;

    var nodes = GetAllNodes(treeView);

    TreeNode node;

 

    //수정 가능한 것인지 판단

    if (!SetValueByTagType(tag, e.ChangedItem.PropertyDescriptor.Name))

    {

        e.ChangedItem.PropertyDescriptor.SetValue(tag, e.OldValue);

        propertyGrid1.Refresh();

        return;

    }

 

 

    //validate

    if (e.ChangedItem.PropertyDescriptor.Name == "DataFieldDisplay")

    {

        //datafield 중복 확인, 이미 변경된 상태라 2개부터 중복임

        if (_list.FindAll(o => o.DataField == DataFieldConverter.List[(string)e.ChangedItem.Value]).Count > 1)

        {

            MessageBox.Show("중복된 필드를 사용하고 있습니다.");

            tag.DataFieldDisplay = (string)e.OldValue;

            return;

        }

    }

           

    // PropTag 변경시 datafield 날림

    if (e.ChangedItem.PropertyDescriptor.Name == "PropTag")

    {

        node = nodes.Find(o => o.Name == tag.ID);

        if (node.Nodes.Count > 0)

        {

            MessageBox.Show("자식 노드가 있으면 변경이 불가능합니다.\n자식 노드를 제거 후 다시 시도해주세요.");

            tag.PropTag = (string)e.OldValue;

            return;

        }

 

        //노드 이미지 변경

        int idx = tag.TAG == "div" ? 0 : 1;

        node.ImageIndex = idx;

        node.SelectedImageIndex = idx;

        node.StateImageIndex = idx;

 

        tag.DataField = null;

        tag.ValidMinLength = null;

        tag.ValidMaxLength = null;

        tag.ValidMinNumber = null;

        tag.ValidMaxNumber = null;

        tag.ValidRegexString = "";

        tag.ValidRegexFlag = "";

        tag.TitleAlign = "";

 

 

        MessageBox.Show("객체종류를 변경하면 저장칼럼을 다시 지정해주셔야합니다.");

    }

    //display 속성 변경시 해당 속성마다 기본값 지정

    if (e.ChangedItem.PropertyDescriptor.Name == "DisplayProperty")

    {

        if ((string)e.ChangedItem.Value == "inline-block" && (tag.Width == "0" || tag.Width == null || tag.Width == ""))

            tag.Width = "200";

        else if ((string)e.ChangedItem.Value == "block")

            tag.Width = null;

        return;

    }

    //validate 숫자만 들어가야하는 곳

    if (intList.Contains(e.ChangedItem.PropertyDescriptor.Name))

    {

        int ivalue = 0;

        if (!string.IsNullOrEmpty((string)e.ChangedItem.Value) && !int.TryParse((string)e.ChangedItem.Value, out ivalue))

        {

            MessageBox.Show("숫자만 넣을 수 있습니다.");

            e.ChangedItem.PropertyDescriptor.SetValue(tag, e.OldValue);

            propertyGrid1.Refresh();

            return;

        }

    }

 

    //disable (해당 칼럼이 값의 변경이 없어야 된다면, e.OldValue 를 다시 할당한다

 

 

 

    //해당 작업을 다 한 뒤에는 트리뷰, 간략모드 를 refresh 한다.

    var selectedNode = nodes.Find(o => o.Name == tag.ID);

    selectedNode.Text = GetTreeNodeText(tag);

 

    RefreshPreviewDataGridView();

}

 

private bool SetValueByTagType(HtmlTag t, string _propertyName)

{

    if (new List<string> { "Width", "Height" }.Contains(_propertyName))

        return true;

    else if (t.ID != "ROOT")

    {

        if (new List<string> { "PropTag", "Title", "DisplayProperty", "PositionDisplay", "Top", "Left" }.Contains(_propertyName))

            return true;

        else if (t.TAG != "div")

        {

            if (new List<string> { "TitleWidth", "MinWidth", "MinHeight", "DataDesc", "DataFieldDisplay", "TcVisible", "ValidErrorMsg", "TcAlignDisplay", "TcHead", "TcWidth" }.Contains(_propertyName))

                return true;

            else if (t.TAG == "textarea" || t.InputType == "text")

            {

                if (new List<string> { "ValidRegexFlagDisplay", "ValidRegexString" }.Contains(_propertyName))

                    return true;

                else if (t.InputType == "text" && new List<string> { "UserInputTypeDisplay" }.Contains(_propertyName))

                    return true;

            }

 

            if (t.InputType != "check")

            {

                if (new List<string> { "ValidDataRequired", "TitleAlignDisplay" }.Contains(_propertyName))

                    return true;

 

                if (t.InputType == "int" || t.InputType == "bigint" || t.InputType == "numeric")

                {

                    if (new List<string> { "ValidMinNumber", "ValidMaxNumber" }.Contains(_propertyName))

                        return true;

                }

                else

                {

                    if (new List<string> { "ValidMinLength", "ValidMaxLength" }.Contains(_propertyName))

                        return true;

                }

 

                if (t.TAG == "select")

                    if (new List<string> { "Category" }.Contains(_propertyName))

                        return true;

            }

 

 

        }

 

 

    }

    return false;

}











Posted by motolies
,